马拉车算法
本文最后更新于 92 天前,如有失效请评论区留言。

O(n)时间处理查询回文串问题

# 将 dfsStr 改造为 t,这样就不需要讨论 n 的奇偶性,因为新串 t 的每个回文子串都是奇回文串(都有回文中心)
# dfsStr 和 t 的下标转换关系:
# (dfsStr_i+1)*2 = ti
# ti/2-1 = dfsStr_i
# ti 为偶数,对应奇回文串(从 2 开始)
# ti 为奇数,对应偶回文串(从 3 开始)
t = '#'.join(['^'] + list(s) + ['$'])

# 定义一个奇回文串的回文半径=(长度+1)/2,即保留回文中心,去掉一侧后的剩余字符串的长度
# halfLen[i] 表示在 t 上的以 t[i] 为回文中心的最长回文子串的回文半径
# 即 [i-halfLen[i]+1,i+halfLen[i]-1] 是 t 上的一个回文子串
halfLen = [0] * (len(t) - 2)
halfLen[1] = 1
# boxR 表示当前右边界下标最大的回文子串的右边界下标+1
# boxM 为该回文子串的中心位置,二者的关系为 r=mid+halfLen[mid]
boxM = boxR = 0
for i in range(2, len(halfLen)):
    hl = 1
    if i < boxR:
        # 记 i 关于 boxM 的对称位置 i'=boxM*2-i
        # 若以 i' 为中心的最长回文子串范围超出了以 boxM 为中心的回文串的范围(即 i+halfLen[i'] >= boxR)
        # 则 halfLen[i] 应先初始化为已知的回文半径 boxR-i,然后再继续暴力匹配
        # 否则 halfLen[i] 与 halfLen[i'] 相等
        hl = min(halfLen[boxM * 2 - i], boxR - i)
    # 暴力扩展
    # 算法的复杂度取决于这部分执行的次数
    # 由于扩展之后 boxR 必然会更新(右移),且扩展的的次数就是 boxR 右移的次数
    # 因此算法的复杂度 = O(len(t)) = O(n)
    while t[i - hl] == t[i + hl]:
        hl += 1
        boxM, boxR = i, i + hl
    halfLen[i] = hl

# t 中回文子串的长度为 hl*2-1
# 由于其中 # 的数量总是比字母的数量多 1
# 因此其在 dfsStr 中对应的回文子串的长度为 hl-1
# 这一结论可用在 isPalindrome 中

# 判断左闭右开区间 [l,r) 是否为回文串  0<=l<r<=n
# 根据下标转换关系得到 dfsStr 的 [l,r) 子串在 t 中对应的回文中心下标为 l+r+1
# 需要满足 halfLen[l + r + 1] - 1 >= r - l,即 halfLen[l + r + 1] > r - l
# 用于检测指定子串是否为回文,这在某些特定应用场景(比如需要频繁检查多个不同子串是否为回文)中很有用
def isPalindrome(l: int, r: int) -> bool:
    return halfLen[l + r + 1] > r - l

模板题:
https://leetcode.cn/problems/longest-palindromic-substring/description/

版权声明:除特殊说明,博客文章均为大块肌原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇