当前位置: 首页 > news >正文

S001 【模板】从前缀函数到KMP应用 字符串匹配 字符串周期

这篇博客为总结的解题流程和模板,如果想要算法具体的原理和数学证明的话请参考:Prefix function. Knuth–Morris–Pratt algorithm

最长相等真前后缀

最长相等前后缀也被称为 Border ,KMP算法就利用了其性质来进行匹配优化。

  • 前缀:从字符串第一个字符开始的子串。
  • 后缀:以字符串最后一个字符结束的子串。
  • 真:长度严格小于原字符串长度。

求法( \(O(n^2)\) ):

def border(s):n = len(s)L = 0for i in range(1, n):  # 长度为 i 上限为 n-1 确保为真前后缀if s[:i] == s[n-i:n]:L = ireturn L

还有更加高效的算法,可以优化到 \(O(n)\) 。正是这个优化产生了 KMP 算法。

前缀函数

在 KMP 中的前缀函数的到数组 \(\pi\) ,其中 \(\pi[i]\) 表示字符串的前缀 \(t[0\dots i]\) 中,最长的相等真前后缀的长度。

如果使用暴力枚举每个子串进行一次 border 函数的话时间复杂度是 \(O(n^3)\)

b = []
for i in range(len(s)):b.append(border(s[:i + 1])) 

通过递推可以在 \(O(n)\) 时间内求出前缀数组 \(\pi\)

  1. 假设我们正在计算 \(\pi[i]\) ,并且已知 \(\pi[i-1]=j\)
  2. 这意味着 \(t[0\dots j-1]\)\(t[0\dots i-1]\) 的最长相等前后缀。
  3. 情况A:如果 \(t[i]==t[j]\) ,那么 \(\pi[i] = j+1\)
  4. 情况B:如果 \(t[i] \ne t[j]\) ,我们需要一个更短的相等前后缀。于是我们可以让 \(j=\pi[j-1]\) ,然后重复 \(t[i]\)\(t[j]\) 的比较过程,直到匹配或 \(j\) 降为 \(0\)

具体代码为

def get_pi(s):n = len(s)pi = [0] * nfor i in range(1, n): j = pi[i - 1]  # 取前一个的位置的piwhile j > 0 and s[i] != s[j]:  # 情况Bj = pi[j - 1]if s[i] == s[j]:  # 情况Aj += 1pi[i] = jreturn pi

应用

模式串匹配

已知模式串 \(t\) 和匹配串 \(s\) ,在预处理完模式串的 \(\pi\) 数组后可以通过双指针匹配。

  1. 指针 \(i\) :始终在 \(s\) 上向右移动,不回退。
  2. 指针 \(j\) :在 \(t\) 上移动,如果匹配 j++ 如果失配 \(j\) 根据 \(\pi\) 数组向左跳,跳到一个可以让前面部分继续匹配的位置。

这个根据 \(\pi\) 数组向左跳的过程可以理解成下面这句有名名的话:

一个人能走的多远不在于他在顺境时能走的多快,而在于他在逆境时多久能找到曾经的自己。

m, n = len(t), len(s)
pi = get_pi(t)j = 0  # 模式串指针
for i in range(n):  # 文本串指针永不回退while j > 0 and s[i] != t[j]:j = pi[j - 1]if s[i] == t[j]:j += 1if j == m:  # 匹配成功# 位置为 i - j + 1 0-basedj = pi[j - 1]  # 继续匹配可能重叠的下一处

求字符串周期

先利用预处理的 \(\pi\) 数组求出所有的 Border ,再根据这些 Border 就可以构造出所有的周期串了。

求字符串所有周期

由于 \(\pi\) 数组记录了最长 Border ,而次长的 Border 可能通过 \(\pi[\pi[n-1]-1]\) 递归求得,因此我们可以不断回跳,求出所有的 Border 后,周期就是 n - Border。

n = len(s)
pi = get_pi(s)b = []
k = pi[n - 1]
while k:b.append(n - k)k = pi[k - 1]
b.append(n)print(*b)

不要忘记了 \(s\) 自身也是周期,然后每个周期串就是 s[:b[i]]

完全循环

\(\pi[n-1]>0\) 基础上,当它的最小正周期 \(n-\pi[n-1]\) 可以被总长度 \(n\) 整除时,存在完全循环。

n = len(s)
pi = get_pi(s)k = n - pi[n - 1]
print("YES" if pi[n - 1] > 0 and n % k == 0 else "NO")

例题

1753 String Matching - CSES 模式串匹配模版

1732 Finding Borders - CSES 求出字符串所有 Border

1733 Finding Periods CSES 求出所有的周期大小

459. 重复的子字符串 - 力扣 判断是否存在完全循环

http://www.jsqmd.com/news/433125/

相关文章:

  • YOLO11 改进 - Mamba _ 集成Mamba-YOLO(AAAI 2025),Mamba-YOLO11-L 替换骨干,破解全局依赖建模难题,实现高效实时检测
  • YOLO11 改进 - Mamba _ 集成Mamba-YOLO(AAAI 2025),Mamba-YOLO11-T 替换骨干,破解全局依赖建模难题,实现高效实时检测
  • 私有部署、安全可控:BeeWorks一体化视频会议解决方案赋能政企高效协同
  • YOLO11 改进 - Mamba _ 集成Mamba-YOLO(AAAI 2025),Mamba-YOLO11-B 替换骨干,破解全局依赖建模难题,实现高效实时检测
  • AWS中东数据中心遭不明物体撞击引发大规模服务中断
  • python核心语法-运算符-类型转换 - 努力-
  • 提示工程远程团队敏捷协作:5个工具让沟通更高效!
  • 问题解决:Oracle VirtualBox创建的虚拟主机不能ping通windows host主机虚拟网卡的ip
  • Qt 捕获应用程序未知异常的方法
  • 异常和自定义错误码使用时机
  • 解读大数据领域结构化数据的性能优化策略
  • YOLO11 改进 - C2PSA _ C2PSA融合Mask Attention掩码注意力,可学习掩码矩阵破解低分辨率特征提取难题 _ 2025 预印
  • 计算资源与AI模型性能提升的关系探讨
  • AI检测会对论文进行误判吗?
  • cf div2 1078 F1
  • 2026城固装修公司排名TOP5权威测评|城固哪家装修公司靠谱?性价比高口碑好首选金匠装饰 - 一个呆呆
  • Python核心语法-Python关键字 - 努力-
  • YOLO11 改进 - C2PSA _ C2PSA融合MSLA多尺度线性注意力(Arxiv2025 ):并行多分支架构融合上下文语义,提升特征判别力
  • 元宵节猜灯谜答题闯关抽奖H5抖音快手微信小程序看广告流量主开源
  • YOLO11 改进 - C2PSA _ C2PSA融合Mona多认知视觉适配器(CVPR 2025):打破全参数微调的性能枷锁:即插即用的提点神器,引领视觉微调新突破
  • react遇坑记
  • 大数据领域存算分离的自动化运维实践
  • Python核心语法-数据类型 - 努力-
  • YOLO11 改进 - C2PSA _ C2PSA融合DiffAttention差分注意力:轻量级差分计算实现高效特征降噪,提升模型抗干扰能力
  • 解锁企业知识图谱的“黑匣子”:OntoEKG重塑本体构建范式,AI赋能数据价值释放
  • YOLO11 改进 - C2PSA EDFFN高效判别频域前馈网络(CVPR 2025):频域筛选机制增强细节感知,优化复杂场景目标检测
  • 高通全新可穿戴芯片组或终结智能手机主导地位
  • YOLO11 改进 - C2PSA _ C2PSA融合EDFFN高效判别频域前馈网络(CVPR 2025):频域筛选机制增强细节感知,优化复杂场景目标检测
  • 大数据处理中的并行计算:原理与性能调优
  • 【预测模型】多种智能算法优化深度极限学习机(GWO-DELM/MVO-DELM/WDO-DELM)Matlab实现