专栏文章

KMP,字符串算法万恶的源头

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mingyskf
此快照首次捕获于
2025/12/02 02:15
3 个月前
此快照最后确认于
2025/12/02 02:15
3 个月前
查看原文
默认下文所有字符串的下标从 11 开始。下文用 s[lr]s[l\dots r] 表示 sl,sl+1,,srs_l,s_{l+1},\dots,s_r 顺次拼接出的字符串,即 ss 在下标区间 [l,r][l,r] 内的子串。
学会 KMP,要先学会求 border。
border 是一个字符串 ss 对应的长度为 s|s| 的数组。对 borderi\text{border}_i 的定义为:在字符串 t=s[1i]t=s[1\dots i] 中,使得 t[1v]=t[iv+1i]t[1\dots v]=t[i-v+1\dots i] 的最大可能值 vv(需要满足 viv\neq i)。
其求法为:假设当前已经求出了 border1,border2,,borderi1\text{border}_1,\text{border}_2,\dots,\text{border}_{i-1},需要求 borderi\text{border}_i,则定义一个值 tptp,初始 tp=borderi1tp=\text{border}_{i-1},进行下面的操作:
  • 如果 stp+1=sis_{tp+1}=s_i,则 borderi\text{border}_i 就是 tptp,赋值并结束操作。
  • 反之,让 tpbordertptp\leftarrow \text{border}_{tp}
  • 如果此时 tp=0tp=0,则 borderi=0\text{border}_i=0,结束操作,否则回到第一步继续操作。
其实就是一个让 s[1tp+1]s[1\dots tp+1]s[itpi]s[i-tp\dots i] 不断匹配的过程,这里的 tptp 满足 s[1tp]=s[itpi1]s[1\dots tp]=s[i-tp\dots i-1]。复杂度感性理解就是线性的。
至此,border 已经求完了。
代码CPP
fo(i,1,n){
    int tp=border[i-1];
    while (tp&&s[i]!=s[tp+1])
        tp=border[tp];
    if (s[i]==s[tp+1])
        tp++;
    border[i]=tp*(i>1);
}
在字符串 ss 中匹配 tt 字符串的做法是类似的,如果 si+1s_{i+1}ttp+1t_{tp+1} 匹配,就 tptp+1tp\leftarrow tp+1;否则就让 tpbordertptp\leftarrow \text{border}_{tp},继续匹配。复杂度也是线性的。
KMP 算法可以用来优化 dp。比如 P3082 这个题,在求出 border 之后,dp 的过程相当于 KMP 匹配字符串的算法,可以优化时间复杂度。

评论

0 条评论,欢迎与作者交流。

正在加载评论...