声明
本文将不断加入例题,稍安勿躁,今天的总结争取9:30写完.
KMP
KMP,中文名字叫字符串匹配,用于解决一类字符串匹配问题.
先下一些定义:
- (s)表示匹配串,(t)表示文本串,字符串匹配用于求(s)在(t)中的出现情况.
- (n)和(m)分别为(s)和(t)的字符串串长.
- (nxt_i)表示对于(s)的前缀(s_{1...i})的最长公共前后缀.
首先我们先想一想(nxt_i)对于求解问题有怎样的帮助.
暴力匹配
我们对于每一个(t_i=s_1)的位置都匹配一次,这样子复杂度为(Theta(n*m))的.
优化
考虑在暴力匹配中其实我们不一定要重新匹配,因为你画一个图感性理解,发现如果匹配到了(s)的(i)前缀,到(i+1)失配,其实是可以从(nxt_i)重新开始匹配的.
这个时候(nxt_i)的好处就体现出来了,我们可以迅速再一次匹配.
代码实现
void KMP(){
nxt[1]=0;int j=0;
for(int i=2;i<=n;i++){
while(j && s[i]!=s[j+1])j=nxt[j];
if(s[i]==s[j+1])j++;nxt[i]=j;
}
j=0;
for(int i=1;i<=m;i++){
while(j && s[j+1]!=t[i])j=nxt[j];
if(t[i]==s[j+1])j++;
if(j==n){ans.push_back(i-n+1);j=nxt[j];}
}
}
扩展KMP
和(KMP)没有半毛钱关系,甚至和(KMP)的思想都没有半毛钱关系.
问题引入
有两个字符串(a),(b),要求输出(b)与(a)的每一个后缀的最长公共前缀。
还是和往常通过一样的,从暴力入手。
暴力求解
枚举每一个(a)的后缀然后与(b)匹配,复杂度(Theta(n*m))的.
其他做法
你当然可以(Hash),但是这与我们讨论的东西无关。
优化
我们大致画一下图(很重要)发现显然之前匹配过的位置如果包含这个(i)后缀的第一个字符,那么可以从那一个最长匹配开始.
剩下的只需要不断像(manacher)一样匹配即可.
代码实现
for(int i=1,l=0,mx=0;t[i];i++){
z[i]=(i<mx)?min(z[i-l],mx-i):0;
while(t[i+z[i]]==t[z[i]])z[i]++;
if(i+z[i]>mx){l=i;mx=i+z[i];}
}
总结
大致就是这样子了,今天还是有点东西的.