标签:两种 ext c++ 进一步 部分 amp src lazy 条件
扩展kmp,求解如下问题:
问s串与t串的每一个后缀的最长公共前缀
容易发现:当某一个最长公共前缀等于s串的长度的时候,其实就是一个s串与t串的kmp匹配问题,因此得名“扩展”kmp。事实上,扩展kmp与普通kmp有一定的区别,两者并不是完全是拓扑关系。即使完全没学过kmp,直接上手学扩展kmp也不是天方夜谭
先尝试通过强化条件,降低一下难度:假如s与t完全相同,扩展kmp该如何实现?
当s与t完全相同,其实问题可以进一步转化为“s与s的每一个后缀的最长公共前缀”。为了方便记录,规定:\(nxt[i]\)表示 s[1..n]与s[i..n]的最长公共前缀长度。
从最浅显易懂的地方入手。nxt[1]=s串长度,这一点我们从定义便可知;nxt[2]则暴力地枚举一遍,\(O(n)\)解决
上述部分的代码
nxt[1]=lenb;
int now=0;
while(now+2<=lenb&&b[now+1]==b[now+2]) now++;
nxt[2]=now;
考虑如何转移,扩展kmp是一个线性算法,因此当我们求解nxt[k]时,nxt[i]\((1\leq i < i)\)已经得到了求解。我们设i+nxt[i]-1的最大值为p,此时的i坐标为p0
记K在nxt[p0]中对应的位置,具体而言k-p0+1,=a,另L=nxt[a]
根据nxt[p0]和nxt[a]的定义,得知所有的绿色区域都是相同的。nxt[p0]的定义可知,两个红色框相同;而nxt[a]的定义可知,蓝框与红框不相同,因此无法继续匹配下去。此时:nxt[k]=nxt[a]
此时由于超出了P的范围,对于P以后的区间的信息是一无所知的,因此此时只有nxt[k]\(\geq\)P-k+1 之后我们需要暴力地枚举下去,直到失配
代码如下:
lor(i,3,lenb){
if(i-1+nxt[i-p+1]<p+nxt[p]-1) nxt[i]=nxt[i-p+1]; // 情况一
else{ // 情况二
int now=max(0,p+nxt[p]-i); //这是为了防止P小于k的情况
while(i+now<=lenb&&b[1+now]==b[i+now]) now++;
nxt[i]=now; p=i; // 暴力枚举后,更新答案
}
}
至此,我们得到了强化条件版:s对于s的所有后缀的最长公共前缀求解
为了区分,我们将这种情况下的匹配记录为extend[i],extend[i]表示s与t[i..n]的最长公共前缀的长度
其实,当我们分析完nxt[]数组的求解之后,会发现原问题已经在无形中被解决了
暴力地预处理extend[1]:
int now=0;
while(1+now<=lena&&1+now<=lenb&&a[1+now]==b[1+now]) now++;
extend[1]=now; p=1;
模仿nxt[]去分类讨论,只是这时候上方s[]的坐标p的含义变更为:p0+extend[p0]-1的最大值
两种分类讨论其实无非就是把原先的两个nxt数组的利用,改为T[]的extend和s[]的nxt。代码部分也是略微修改即可。为了避免累赘本人太懒直接上代码
lor(i,2,lena){
if(i-1+nxt[i-p+1]<p+extend[p]-1) extend[i]=nxt[i-p+1]; // 情况一
else{ // 情况二
int now=max(0,p+extend[p]-i);
while(1+now<=lenb&&i+now<=lena&&b[1+now]==a[i+now]) now++;
extend[i]=now; p=i;
}
}
虽然后缀数组可以取代扩展kmp,但是如同树状数组和线段树的关系,扩展kmp有着常数小,容易写(虽然我觉得后缀数组更好写)的优点。
此外经过实测\(10^6\)级随机数据,发现速度从快到慢为:mp> kmp> ex_kmp ...
这..可能就是强大的代价吧 ( _ _)ノ|
标签:两种 ext c++ 进一步 部分 amp src lazy 条件
原文地址:https://www.cnblogs.com/ticmis/p/13211149.html