《剑指offer》面试题20:回文子字符串的个数

"""
题目:给定一个字符串,请问该字符串中有多少个回文连续子字符串?例如,字符串'ABC'有3个回文子字符串,分别为'A','B','C';
     而字符串‘AAA'有6个回文子字符串,分别为’A','A','A','AA','AA','AAA'
解题思路:这个题上来一看,我的脑子里就把它翻译为求子字符串为回文的个数。
        前面已经做了很多子数组或者子字符串的题了,那一定是要用到双指针了。核心逻辑就是如何移动双指针。
        首先确定双指针的起始位置和方向:
        如果按照之前的两道回文的题目的逻辑,将双指针放在字符串的两头,然后向中间移动,思考一下就会发现移动完以后,只能得到一个回文
        因此参考前面满足条件的子字符串的个数的逻辑(第14~17题),指针P1和指针P2应都从左边向右边移动。
        然后确定双指针的移动算法:
        回文不仅能通过从两端往中间看是否两端相等,也可以从中间开始逐步向两边扩散看两端是否相等。
        因此参考这个逻辑,我们可以将指针逐个指向子回文的中间,然后判断它两端的字符是否一致,直到字符串两端。
        例如,刚开始指向第一个元素,然后向两端扩散,扩散完后,发现只有它自己的回文。然后指向第二个元素,如此循环,直到指向最后一个元素。
        这里要注意的是,回文的长度可以为奇数,也可以为偶数。长度为奇数的的回文对称中心只有一个字符,长度为偶数的字符串对称中心有两个字符。
        因此如果是奇数字符串,两个指针应都从一个位置开始。如果是偶数字符串,指针P2要比指针P1多1.
"""
import re


def mirror_count_from_middle(t, start, end):
    count = 0
    while start >= 0 and end <= len(t)-1 and t[start] == t[end]:
        start -= 1
        end += 1
        count += 1
    return count


def mirror_count(t):
    pattern = re.compile('[a-zA-Z0-9]')
    new_t = pattern.findall(t.upper())
    count = 0
    for i in range(len(new_t)):
        count += mirror_count_from_middle(new_t, i, i)
        count += mirror_count_from_middle(new_t, i, i+1)
    return count

t = 'aaa'
t1 = 'abc'
t2 = 'asdffgg'
t3 = ''
print(mirror_count(t))
print(mirror_count(t1))
print(mirror_count(t2))
print(mirror_count(t3))
### Python 中处理回文子字符串的方法 #### 动态规划方法 动态规划是一种有效解决最长回文子串问题的方式。通过构建二维数组 `dp` 来记录子串是否为回文。 ```python def longest_palindromic_substring(s: str) -> str: n = len(s) if n == 0: return "" dp = [[False] * n for _ in range(n)] start, end = 0, 0 for i in range(n): for j in range(i): dp[j][i] = (s[i] == s[j]) and ((i - j < 2) or dp[j + 1][i - 1]) if dp[j][i] and (end - start) < (i - j): start, end = j, i dp[i][i] = True return s[start:end + 1] ``` 这种方法的时间复杂度为 O(n²),空间复杂度也为 O(n²)[^1]。 #### Manacher 算法 Manacher 算法能够在线性时间内解决问题,其核心在于利用已知的回文信息减少不必要的比较次数。 ```python def manachers_algorithm(s: str) -> str: T = '#'.join('^{}$'.format(s)) P = [0] * len(T) C = R = 0 for i in range(1, len(T) - 1): mirr = 2 * C - i if R > i: P[i] = min(R - i, P[mirr]) while T[i + 1 + P[i]] == T[i - 1 - P[i]]: P[i] += 1 if i + P[i] > R: C, R = i, i + P[i] max_len, center_index = max((n, i) for i, n in enumerate(P)) return s[(center_index - max_len) // 2 : (center_index + max_len) // 2] ``` 此算法通过对原字符串预处理,在每个字符间插入特殊符号,使得所有可能存在的回文都变成奇数长度,从而简化了中心扩展的过程[^2]。 #### 回溯法求解全部回文分割方案 当目标是从给定字符串中找出所有的回文分割方式时,则可以采用回溯策略: ```python from typing import List def partition(s: str) -> List[List[str]]: result = [] def backtrack(start=0, path=[]): if start >= len(s): result.append(path[:]) return for end in range(start + 1, len(s) + 1): substring = s[start:end] if substring == substring[::-1]: path.append(substring) backtrack(end, path) path.pop() backtrack() return result ``` 这段代码实现了对输入字符串的所有合法回文划分组合进行枚举的功能[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值