首页 > 编程语言 >【轻松掌握数据结构与算法】字符串算法(String Algorithms)

【轻松掌握数据结构与算法】字符串算法(String Algorithms)

时间:2025-01-15 09:58:50浏览次数:3  
标签:node s2 String 示例 pattern self 算法 Algorithms text

字符串算法概述

字符串匹配算法是计算机科学中的一个重要领域,主要用于在文本中查找特定模式(子字符串)的出现位置。这些算法在文本编辑器、搜索引擎、生物信息学等领域有广泛的应用。

暴力法(Brute Force Method)

暴力法是最直接的字符串匹配算法,它通过逐个字符比较来查找模式在文本中的所有出现位置。

示例代码

def brute_force_search(text, pattern):
    n = len(text)
    m = len(pattern)
    for i in range(n - m + 1):
        j = 0
        while j < m and text[i + j] == pattern[j]:
            j += 1
        if j == m:
            return i  # 模式在文本中的起始位置
    return -1  # 模式未找到

# 示例输入输出
text = "ABC ABCDAB ABCDABCDABDE"
pattern = "ABCDABD"
print(brute_force_search(text, pattern))  # 输出: 15

Rabin-Karp 字符串匹配算法

Rabin-Karp 算法使用哈希函数来快速查找模式在文本中的所有出现位置。它通过计算模式和文本子字符串的哈希值来减少不必要的比较。

示例代码

def rabin_karp_search(text, pattern, prime, d):
    n = len(text)
    m = len(pattern)
    p = 0  # 模式的哈希值
    t = 0  # 文本子字符串的哈希值
    h = 1  # 哈希函数的最高次项系数

    # 计算 h, p 和 t
    for i in range(m - 1):
        h = (h * d) % prime
    for i in range(m):
        p = (d * p + ord(pattern[i])) % prime
        t = (d * t + ord(text[i])) % prime

    # 滑动窗口查找模式
    for i in range(n - m + 1):
        if p == t:
            # 通过逐个字符比较确认匹配
            for j in range(m):
                if text[i + j] != pattern[j]:
                    break
            else:
                return i  # 模式在文本中的起始位置
        if i < n - m:
            t = (d * (t - ord(text[i]) * h) + ord(text[i + m])) % prime
            if t < 0:
                t += prime
    return -1  # 模式未找到

# 示例输入输出
text = "ABC ABCDAB ABCDABCDABDE"
pattern = "ABCDABD"
prime = 101
d = 256
print(rabin_karp_search(text, pattern, prime, d))  # 输出: 15

使用有限自动机的字符串匹配

有限自动机(Finite Automaton, FA)是一种状态机,用于在文本中查找模式。它通过预处理模式构建一个状态转移表,然后在文本中进行匹配。

示例代码

def compute_transition_function(pattern, alphabet):
    m = len(pattern)
    transition = {}
    for q in range(m + 1):
        for a in alphabet:
            k = min(m + 1, q + 2)
            while k > 0 and not (pattern[:q] + a).endswith(pattern[:k]):
                k -= 1
            transition[(q, a)] = k
    return transition

def finite_automaton_search(text, pattern):
    n = len(text)
    m = len(pattern)
    alphabet = set(text)
    transition = compute_transition_function(pattern, alphabet)
    q = 0
    for i in range(n):
        q = transition.get((q, text[i]), 0)
        if q == m:
            return i - m + 1  # 模式在文本中的起始位置
    return -1  # 模式未找到

# 示例输入输出
text = "ABC ABCDAB ABCDABCDABDE"
pattern = "ABCDABD"
print(finite_automaton_search(text, pattern))  # 输出: 15

KMP 算法

KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,它通过预处理模式构建一个部分匹配表(Partial Match Table),然后在文本中进行匹配,避免了不必要的回溯。

示例代码

def compute_lps_array(pattern):
    m = len(pattern)
    lps = [0] * m
    length = 0
    i = 1
    while i < m:
        if pattern[i] == pattern[length]:
            length += 1
            lps[i] = length
            i += 1
        else:
            if length != 0:
                length = lps[length - 1]
            else:
                lps[i] = 0
                i += 1
    return lps

def kmp_search(text, pattern):
    n = len(text)
    m = len(pattern)
    lps = compute_lps_array(pattern)
    i = 0  # 文本索引
    j = 0  # 模式索引
    while i < n:
        if pattern[j] == text[i]:
            i += 1
            j += 1
        if j == m:
            return i - j  # 模式在文本中的起始位置
        elif i < n and pattern[j] != text[i]:
            if j != 0:
                j = lps[j - 1]
            else:
                i += 1
    return -1  # 模式未找到

# 示例输入输出
text = "ABC ABCDAB ABCDABCDABDE"
pattern = "ABCDABD"
print(kmp_search(text, pattern))  # 输出: 15

Boyer-Moore 算法

Boyer-Moore 算法是一种高效的字符串匹配算法,它通过从右向左比较字符,利用坏字符规则和好后缀规则来跳过不必要的比较。

示例代码

def bad_char_heuristic(pattern, alphabet):
    m = len(pattern)
    bad_char = {char: -1 for char in alphabet}
    for i in range(m):
        bad_char[pattern[i]] = i
    return bad_char

def good_suffix_heuristic(pattern):
    m = len(pattern)
    suffix = [0] * m
    prefix = [False] * m
    for i in range(m - 1):
        j = i
        while j >= 0 and pattern[j] == pattern[m - 1 - i + j]:
            j -= 1
        suffix[i] = j + 1
        if j == -1:
            prefix[i] = True
    return suffix, prefix

def boyer_moore_search(text, pattern):
    n = len(text)
    m = len(pattern)
    alphabet = set(text)
    bad_char = bad_char_heuristic(pattern, alphabet)
    suffix, prefix = good_suffix_heuristic(pattern)
    i = 0
    while i <= n - m:
        j = m - 1
        while j >= 0 and pattern[j] == text[i + j]:
            j -= 1
        if j < 0:
            return i  # 模式在文本中的起始位置
        x = j - bad_char.get(text[i + j], -1)
        y = 0
        if j < m - 1:
            y = m - 1 - j + suffix[j]
            if not prefix[j]:
                y = m - 1 - j + suffix[j]
            else:
                y = m - suffix[m - 1]
        i += max(x, y)
    return -1  # 模式未找到

# 示例输入输出
text = "ABC ABCDAB ABCDABCDABDE"
pattern = "ABCDABD"
print(boyer_moore_search(text, pattern))  # 输出: 15

存储字符串的数据结构

哈希表(Hash Tables)

哈希表是一种基于哈希函数的数据结构,用于存储键值对。它通过哈希函数将键映射到表中的位置,然后将键值对存储在该位置。

二叉搜索树(Binary Search Trees)

二叉搜索树是一种有序的二叉树,每个节点的值大于其左子树的所有节点值,小于其右子树的所有节点值。它支持高效的插入、删除和查找操作。

字典树(Tries)

字典树是一种用于存储字符串集合的树形数据结构。每个节点代表一个字符,从根节点到某个节点的路径代表一个前缀。字典树支持高效的前缀查找和字符串插入操作。

三叉搜索树(Ternary Search Trees)

三叉搜索树是一种结合了二叉搜索树和哈希表优点的数据结构。每个节点有三个子节点:左子节点、中子节点和右子节点。它支持高效的插入、删除和查找操作。

后缀树(Suffix Trees)

后缀树是一种用于存储字符串所有后缀的树形数据结构。每个节点代表一个后缀,从根节点到某个节点的路径代表一个后缀。后缀树支持高效的子字符串查找和模式匹配操作。

示例代码:字典树(Trie)

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end_of_word = False

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word):
        node = self.root
        for char in word:
            if char not in node.children:
                node.children[char] = TrieNode()
            node = node.children[char]
        node.is_end_of_word = True

    def search(self, word):
        node = self.root
        for char in word:
            if char not in node.children:
                return False
            node = node.children[char]
        return node.is_end_of_word

    def starts_with(self, prefix):
        node = self.root
        for char in prefix:
            if char not in node.children:
                return False
            node = node.children[char]
        return True

# 示例输入输出
trie = Trie()
trie.insert("apple")
print(trie.search("apple"))  # 输出: True
print(trie.search("app"))    # 输出: False
print(trie.starts_with("app"))  # 输出: True

示例代码:后缀树(Suffix Tree)

class SuffixTreeNode:
    def __init__(self, start, end, suffix_link=None):
        self.start = start
        self.end = end
        self.suffix_link = suffix_link
        self.children = {}

class SuffixTree:
    def __init__(self, text):
        self.text = text
        self.root = SuffixTreeNode(-1, -1)
        self.build()

    def build(self):
        for i in range(len(self.text)):
            self.add_suffix(i)

    def add_suffix(self, start):
        node = self.root
        for j in range(start, len(self.text)):
            if self.text[j] not in node.children:
                node.children[self.text[j]] = SuffixTreeNode(j, len(self.text))
            node = node.children[self.text[j]]

    def search(self, pattern):
        node = self.root
        for char in pattern:
            if char not in node.children:
                return False
            node = node.children[char]
        return True

# 示例输入输出
text = "banana"
suffix_tree = SuffixTree(text)
print(suffix_tree.search("ana"))  # 输出: True
print(suffix_tree.search("apple"))  # 输出: False

字符串算法: 问题与解决方案

问题1:实现一个函数,检查一个字符串是否是另一个字符串的子串

问题描述:给定两个字符串 s1s2,检查 s1 是否是 s2 的子串。

示例代码

def is_substring(s1, s2):
    return s1 in s2

# 示例输入输出
s1 = "abc"
s2 = "abcde"
print(is_substring(s1, s2))  # 输出: True

s1 = "xyz"
s2 = "abcde"
print(is_substring(s1, s2))  # 输出: False

问题2:实现一个函数,找到一个字符串在另一个字符串中所有出现的位置

问题描述:给定两个字符串 s1s2,找到 s1s2 中所有出现的位置。

示例代码

def find_all_occurrences(s1, s2):
    occurrences = []
    start = 0
    while start < len(s2):
        pos = s2.find(s1, start)
        if pos != -1:
            occurrences.append(pos)
            start = pos + 1
        else:
            break
    return occurrences

# 示例输入输出
s1 = "abc"
s2 = "abcdeabc"
print(find_all_occurrences(s1, s2))  # 输出: [0, 5]

s1 = "xyz"
s2 = "abcde"
print(find_all_occurrences(s1, s2))  # 输出: []

问题3:实现一个函数,检查一个字符串是否是回文

问题描述:给定一个字符串 s,检查它是否是回文。

示例代码

def is_palindrome(s):
    return s == s[::-1]

# 示例输入输出
s = "racecar"
print(is_palindrome(s))  # 输出: True

s = "hello"
print(is_palindrome(s))  # 输出: False

问题4:实现一个函数,找到一个字符串的最长回文子串

问题描述:给定一个字符串 s,找到它的最长回文子串。

示例代码

def longest_palindromic_substring(s):
    if not s:
        return ""
    n = len(s)
    start, max_length = 0, 1

    def expand_around_center(left, right):
        while left >= 0 and right < n and s[left] == s[right]:
            left -= 1
            right += 1
        return left + 1, right - 1

    for i in range(n):
        left1, right1 = expand_around_center(i, i)
        left2, right2 = expand_around_center(i, i + 1)

        if right1 - left1 + 1 > max_length:
            start = left1
            max_length = right1 - left1 + 1
        if right2 - left2 + 1 > max_length:
            start = left2
            max_length = right2 - left2 + 1

    return s[start:start + max_length]

# 示例输入输出
s = "babad"
print(longest_palindromic_substring(s))  # 输出: "bab" 或 "aba"

s = "cbbd"
print(longest_palindromic_substring(s))  # 输出: "bb"

问题5:实现一个函数,找到两个字符串的最长公共子串

问题描述:给定两个字符串 s1s2,找到它们的最长公共子串。

示例代码

def longest_common_substring(s1, s2):
    m, n = len(s1), len(s2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    max_length = 0
    end = 0

    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i - 1] == s2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
                if dp[i][j] > max_length:
                    max_length = dp[i][j]
                    end = i
            else:
                dp[i][j] = 0

    return s1[end - max_length:end]

# 示例输入输出
s1 = "ABABC"
s2 = "BABCA"
print(longest_common_substring(s1, s2))  # 输出: "BABC"

s1 = "ABC"
s2 = "DEF"
print(longest_common_substring(s1, s2))  # 输出: ""

问题6:实现一个函数,找到一个字符串的最短唯一前缀

问题描述:给定一个字符串数组 strs,找到每个字符串的最短唯一前缀。

示例代码

class TrieNode:
    def __init__(self):
        self.children = {}
        self.count = 0

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word):
        node = self.root
        for char in word:
            if char not in node.children:
                node.children[char] = TrieNode()
            node = node.children[char]
            node.count += 1

    def shortest_unique_prefix(self, word):
        node = self.root
        prefix = ""
        for char in word:
            prefix += char
            node = node.children[char]
            if node.count == 1:
                return prefix
        return prefix

def shortest_unique_prefixes(strs):
    trie = Trie()
    for word in strs:
        trie.insert(word)
    return [trie.shortest_unique_prefix(word) for word in strs]

# 示例输入输出
strs = ["dog", "cat", "apple", "apricot", "fish"]
print(shortest_unique_prefixes(strs))  # 输出: ["d", "c", "app", "apr", "f"]

问题7:实现一个函数,检查一个字符串是否可以通过删除一些字符变成另一个字符串的子序列

问题描述:给定两个字符串 s1s2,检查 s1 是否可以通过删除一些字符变成 s2 的子序列。

示例代码

def is_subsequence(s1, s2):
    iter_s2 = iter(s2)
    return all(char in iter_s2 for char in s1)

# 示例输入输出
s1 = "abc"
s2 = "ahbgdc"
print(is_subsequence(s1, s2))  # 输出: True

s1 = "axc"
s2 = "ahbgdc"
print(is_subsequence(s1, s2))  # 输出: False

问题8:实现一个函数,找到一个字符串的所有回文子串

问题描述:给定一个字符串 s,找到它的所有回文子串。

示例代码

def all_palindromic_substrings(s):
    n = len(s)
    dp = [[False] * n for _ in range(n)]
    palindromes = []

    for i in range(n):
        dp[i][i] = True
        palindromes.append(s[i:i+1])

    for start in range(n-1, -1, -1):
        for end in range(start+1, n):
            if s[start] == s[end]:
                if end - start == 1 or dp[start+1][end-1]:
                    dp[start][end] = True
                    palindromes.append(s[start:end+1])

    return palindromes

# 示例输入输出
s = "abc"
print(all_palindromic_substrings(s))  # 输出: ['a', 'b', 'c']

s = "aaa"
print(all_palindromic_substrings(s))  # 输出: ['a', 'a', 'a', 'aa', 'aa', 'aaa']

以上是“String Algorithms”中的一些常见问题及其解决方案。这些问题涵盖了字符串匹配、子串查找、回文检查、最长公共子串、最短唯一前缀和子序列检查等多个方面。希望这些示例代码和输入输出能帮助您更好地理解和应用字符串算法。

总结

字符串匹配算法在计算机科学中有广泛的应用。暴力法、Rabin-Karp 算法、有限自动机、KMP 算法和 Boyer-Moore 算法是几种常见的字符串匹配算法,每种算法都有其特定的用途和优缺点。此外,哈希表、二叉搜索树、字典树、三叉搜索树和后缀树是用于存储和处理字符串的常用数据结构。希望这些示例代码和输入输出能帮助您更好地理解和应用字符串算法。

标签:node,s2,String,示例,pattern,self,算法,Algorithms,text
From: https://blog.csdn.net/Whoisbug/article/details/145127386

相关文章

  • 算法面试准备 - 手撕系列第二期 - 交叉熵损失(Cross Entropy Loss)
    算法面试准备-手撕系列第二期-交叉熵损失(CrossEntropyLoss)目录算法面试准备-手撕系列第二期-交叉熵损失(CrossEntropyLoss)交叉熵原理图交叉熵损失实现代码-不同y_pre版本参考交叉熵原理图Softmax原理图交叉熵损失实现代码-不同y_pre版本......
  • 期望最大化算法:机器学习中的隐变量与参数估计的艺术
    引言在机器学习和统计学领域,许多实际问题涉及到含有隐变量的概率模型。例如,在图像识别中,图像的语义信息往往是隐变量,而我们能观测到的只是图像的像素值;在语音识别中,语音对应的文本内容是隐变量,观测数据则是语音信号。期望最大化(Expectation-Maximization,简称EM)算法作为一......
  • 通过一个算法的设计来了解栈的一些应用
    目录1.前言2.步骤3.代码实现4.测试5.运行结果6.一些思考7.一些应用示例1.前言掌握堆栈的基本原理掌握堆栈的存储结构掌握堆栈的进栈、出栈;判断栈空的实现方法掌握应用堆栈实现括号匹配的原理和实现方法;熟悉python语言编程熟练使用python语言实现堆栈的进栈Pus......
  • [需要复习的算法]算法目录
    1.十分基础1.算法1.枚举上链接!2.模拟上链接!3.分治上链接!4.贪心上链接!5.二分上链接!6.倍增上链接!7.排序上链接!比较基础的几种算法,多种算法依托在这几种思想上。要求:集合为一篇博客产出2.数据结构1.树 李老师的PDF2.图李老师的PDF3.栈 上链接!+李老师的PDF4......
  • 一个算法题目的探索
    首先提出一个简单的问题,之后在此基础上一步步进行拓展,整体上从易到难,逐渐深入。问题一给定\(n\)个区间\([l_i,r_i]\),选出至多\(2\)个两两不重叠的区间\([start_i,end_i]\),每个区间由\([l_x,r_y]\)组成(\(y\gex\)),最大化\(\sum(end_i-start_i)\)分析将\(n\)个区间......
  • 【优先算法】思还故里闾,欲归道无因 - 前缀和
    本篇博客给大家带来的是前缀和算法的知识点,也是一样通过OJ题理解,掌握,应用该算法.......
  • 令人惊艳的算法分享!
    惊艳的算法引言你是否曾想过,是什么让计算机能够如此快速而高效地处理信息?这背后恰恰是算法的功劳。作为计算机科学的基石,算法不仅是解决问题的工具,更是推动技术进步的动力。在这篇文章中,我们将探讨几种经典和新兴的算法,揭示它们是如何颠覆我们对计算的认识并激发创新的。......
  • 排序算法专题总结
    分治基础-二分查找:二分查找是一种高效的查找算法先找到数组的中间位置mid,判断(1)如果要找的数x==a[mid]找到了,mid就是位置(2)如果要找的教x>a[mid],说明要找的数在后一半,递归在后一半找(3)如果要找的数x<a[mid],说明要找的数在前一半,递归在前一半找在下标为left~right之间的......
  • 【一看就会】路径规划算法【一】——广度优先,深度优先,Dijkstra、A*、D*
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、输入输出1.输入环境约束条件目标其他2.输出二、广度优先搜索——BFS三、深度优先搜索——DFS四、Dijkstra五、A*六、D*1.初始路径规划(环境未变化)2.环境变化3.动态调整1.受影响节点标记2......
  • 算法-高精度问题(带图详细解读~)
    今天来分享四道大数运算的模板题.目录1.大数相加2.大数相减3.大数相乘4.大数相除1.大数相加题目链接:LINK基本思路:存入数组,模拟运算.逆序字符串补零操作依次取数据,依次相加3-1加:(t-ret=s1[i]+s2[i]+carry)%10;3-2进:(t-ret=s1[i]+......