📜  回文子串查询(1)

📅  最后修改于: 2023-12-03 15:23:01.929000             🧑  作者: Mango

回文子串查询

回文指的是正反顺序读起来都一样的字符串,例如“level”,“racecar”,“deed”等。

回文子串查询是指找出给定字符串中所有的回文子串。

解法一:暴力枚举

最直观的想法是枚举所有子串,然后判断是否为回文。这种方法的时间复杂度为 $O(n^3)$,不适合处理较长的字符串。

代码片段:

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

def find_all_palindromes(s: str) -> List[str]:
    n = len(s)
    res = []
    for i in range(n):
        for j in range(i+1, n+1):
            if is_palindrome(s[i:j]):
                res.append(s[i:j])
    return res
解法二:中心扩展法

回文串是以中心对称的,因此可以枚举回文串的中心,然后向两边扩展。

注意,回文串的中心可能是一个字符,也可能是两个字符之间的空隙,因此中心的个数是 $2n-1$ 个。

代码片段:

def find_all_palindromes(s: str) -> List[str]:
    n = len(s)
    res = []
    for i in range(2*n-1):
        l, r = i//2, i//2+i%2
        while l >= 0 and r < n and s[l] == s[r]:
            res.append(s[l:r+1])
            l -= 1
            r += 1
    return res

时间复杂度为 $O(n^2)$。

解法三:Manacher

Manacher 算法是解决回文串问题的经典算法,时间复杂度为 $O(n)$。

Manacher 算法的核心思想是利用已经计算出来的回文信息,尽可能地减少重复计算。

具体来说,定义 $p_i$ 表示以位置 $i$ 为中心的最长回文半径,$mx$ 表示当前所有回文子串的右端点中最大者,$id$ 表示所有回文子串中右端点最大者的位置。

则有以下两种情况:

  1. $i < mx$,则 $p_i = \min(p_{2id-i}, mx-i)$,这里的 $2id-i$ 是 $i$ 关于中心点的对称点。
  2. $i \ge mx$,则只能暴力匹配,直到找到最长的回文串。同时更新 $mx$ 和 $id$。

代码片段:

def find_all_palindromes(s: str) -> List[str]:
    t = '^#' + '#'.join(s) + '#$'
    n = len(t)
    p = [0] * n
    id, mx = 0, 0
    for i in range(1, n-1):
        if i < mx:
            p[i] = min(p[2*id-i], mx-i)
        while t[i+p[i]+1] == t[i-p[i]-1]:
            p[i] += 1
        if i + p[i] > mx:
            mx = i + p[i]
            id = i
    res = []
    for i in range(1, n-1):
        l, r = (i-p[i])//2, (i+p[i])//2
        res.append(s[l:r])
    return res
总结

回文子串查询是一个常见的字符串问题,有多种解法可供选择。暴力枚举的时间复杂度较高,不适合处理较长的字符串。中心扩展法的时间复杂度为 $O(n^2)$,相对来说比较高效。Manacher 算法的时间复杂度为 $O(n)$,是最优解法。

代码片段中的函数签名为示例,具体实现可以根据需要进行调整。