Z算法(EXKMP)
爆肝计划
刚开始接触时,疑惑于Z算法和EXKMP,二者的作用基本相似,我就想,既然起了不一样的名字,那么他们的解题思路必然不一样吧。
我又经过了网上大量收集资料,发现有的大佬是两个和一块成一个感念,有的是单独讲一个,这让我小蒟蒻很为难,最后在大佬的指路下去了去了OI WIKI,然后知道他俩是合在一块的,连接再此。
但是EXKMP多了next数组,为了方便,在这里就开始讲z算法吧
1.首先我们要明白z算法是一种用于字符串区配的算法,是对字符串S,O(n)地求出S的全体后缀与S自身的最长公共前缀的长度,记录在数组z[]中(z[i]即后缀 i与S的最长公共前缀的长度)。
2.话不多说,上图(字丑,见谅)
在这里插入图片描述
从上面我们可以得到这么规律
1.假设i>R,则说明不存在一个结束于i或者i之后的串,同时这个串本身也为s的一个前缀,否则R不应该小于i。对于这种情况,需要重新计算新的L与R,令L=R=i,暴力比较s与suffix(i),得到z[i]=R-i+1=R-L+1。
2.此时i<=R,令k=i-L,可以断言z[i]>=min(z[k],R-i+1)。因为根据L与R的含义,此时我们可以将L到R视作为字符串的前缀,那么i相对于L的偏移量为k。
如果z[k]<R-i+1,则z[i]必然等于z[k],基于此时,s[k,k+z[k]-1]是s[i,R]的一个前缀,同时在这种情况下L与R不变。
如果z[k]>=R-i+1,根据R的含义可知s[R+1]!=s[R-L+1],z[k]中大于R-i+1的匹配信息因为s[R+1]!=s[R-L+1]而无效,但这并不意味着s[R+1]!=s[R-i+1],此时根据z[k]可以断言z[i]至少是R-i+1,是否可以更大需要再进行计算,令L=i,更新R值,并得到此时的z[i]。
目前并没有什么只用这算法的例题,那么先把模板代码贴上把,能够实现求z数组
void z_init(){//求z数组
z[1]=n;//特殊处理z[1]
int zl=0,zr=0;//右端点最大
for(int i=2;i<=n;i++)//从i=2递推到i=n
if(zr<i){//第1种情况
z[i]=0;
while(i+z[i]<=n&&a[i+z[i]]==a[1+z[i]])z[i]++;//直接向后暴力匹配
if(z[i])zl=i,zr=i+z[i]-1;//更新右端点
}
else if(i+z[i-zl+1]<=zr)z[i]=z[i-zl+1];//第2种情况的第1种情况
else{//第2种情况的第1种情况
z[i]=zr-i+1;//z[i]至少有zr-i+1
while(i+z[i]<=n&&a[i+z[i]]==a[1+z[i]])z[i]++;//后面再暴力匹配
zl=i;zr=i+z[i]-1;//更新右端点
}
}
欢迎留言哟ヾ(≧▽≦*)o