【字符串】【KMP】
【字符串】【KMP】
0. 引入
先看一道模板题:
【模板】KMP字符串匹配
要求一个字符串(称为模式串)在另一个字符串(称为文本串)中的所有出现位置,朴素的方法是对于文本串的每一位,检查以该位为起点,是否能匹配到一个模式串。复杂度O(NM)
当然我们可以用字符串哈希优化到线性,但有更准确的做法:KMP
1. KMP
KMP的基本功能就是进行单个字符串匹配(多个要用到AC自动机)
KMP有两种理解方式:一种是静态的Lborder,一种是动态的(失配后指针移向哪一位),这里主要讲前者。
2. 定义:
一个字符串的border定义为既是其前缀,也是其后缀,但不为其本身的串。
一个字符串的Lborder定义为其最长的border
3. 性质:
- border的border还是border
利用这个性质,要找到一个串的所有border,只要不停的跳这个串的Lborder即可。
因此,一个串所有前缀的Lborder构成一棵树,其中根节点为0。很多题目可以利用这个树形结构来解题。
2.每个border都与一个不严格循环节一一对应。
也就是说,如果m是长为n的字符串S的一个border,那么n-m就是S的一个不严格循环节,反之亦然。
因此找到一个穿的所有不严格循环节,只要枚举一个串的所有border即可。
而严格的循环节就是使得(n-m)|n的长为m的border。
4. 字符串匹配
- nxt数组
nxt[i]表示字符串长度为i的前缀的Lborder的长度
如何快速求出nxt数组?
采用递推,假设已知nxt[1~(i-1)],若求nxt[i],可令j=nxt[i-1]。
检查若s[j+1]==s[i],则令nxt[i]=j+1,否则令j=nxt[j],继续检查。重复这一步直到匹配成功或j=0
可以证明复杂度O(|S|)
CODE:
void getnxt() { for(int i=2;i<=m;++i) { int j=nxt[i-1]; while(j&&b[j+1]!=b[i]) j=nxt[j]; if(b[j+1]==b[i]) nxt[i]=j+1; } }
- 匹配
与求nxt的过程类似,记i表示当前文本串匹配到第i位,j表示模式串最多前j位能与文本串前i位构成的子串中后j位进行匹配。
当i++时,则检查若s[j+1]==s[i],则令j++,否则令j=nxt[j],继续检查。重复这一步直到匹配成功或j=0
若j=m,则记录答案,并令j=nxt[j]。
可以复杂度O(|N|+|M|)
CODE:
void kmp() { for(int i=1,j=0;i<=n;++i) { while(j&&b[j+1]!=a[i]) j=nxt[j]; if(b[j+1]==a[i]) j++; if(j==m) { write(i-m+1);puts(""); j=nxt[j]; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通