http://chaoswork.com/blog/2011/06/14/kmp%E7%AE%97%E6%B3%95%E5%B0%8F%E7%BB%93/
next[0]=-1;j=-1; for(i=0;i<m;) { while(j>=0 && p[i]!=p[j])j=next[j]; i++; j++; next[i]=j; } for(i=0,flag=0,j=0;i<n;) { while(j>=0 && p[j]!=t[i])j=next[j]; i++; j++;//已经匹配了模式串的多少个 if(j==m)//匹配成功 }
接下来需要更加深入地了解next数组,许多题目需要用到它的定义来预处理字符串:
xdu 1154
显然,用2次kmp处理处前缀和后缀在各点的匹配情况,用dp记录符合要求的子串,有几个要注意的地方:(调了2个小时T_T)
1.前缀的其实位置不但要在后缀的前面,终止位置不能超过后缀的终止位置,也就是说前缀不能包含后缀.
2.
for(i=0,flag=0,j=0;i<n;) { while(j>=0 && p[j]!=t[i])j=next[j]; i++;j++;
if(j==m)//若在此处记录则会出现bug,因为此时匹配完成点是i-1 }
CF也有一道与上面类似的题,需要用kmp预处理最左端前缀和最右端后缀
http://codeforces.com/contest/149/problem/E
循环节问题
它还能用来求周期字符串的循环节HDU1358:
性质:
当且仅当len%(len-next[len])==0时,str[next[len]~len-1]为最小循环节
要证明它需要说明3点:
1.一个字符串str是周期串,假设s1为它的循环节,则str=s1 s1...s1(n个)
推导出=>len%(len-next[len])==0成立.
2.next[len]~len-1为s1 ,len%(len-next[len])==0时
推导出=>str为周期串,s1为最小循环节
3.如何保证是最小的.
证明:
1.由next的性质知道,s[1~next[len]-1]与s[1~len-1]有最长的相等的前缀和后缀s,很显然s就是n-1个s1了.
2.设s1的长度为L,由于len%L==0 , str可以分解成若干个长度为L的小串,设它们从左到右依次为
a1 , a2 ... an
根据匹配关系得
a1=a2;
a2=a3;
..
an-1=an;
因此a1=a2=a3...=an;
3.next[len]保证了前缀与后缀最大化,如果循环节s1存在而s1内还有循环节s1',则next[len]可以向后移动,与定义矛盾.
相等的循环同构问题
hdu3374 string problem
分析:
最大和最小的循环同构字符串可以分开处理,求位置可以利用最小最大表示法(03wc论文 周源)
求出现的次数可以用kmp扫一遍,但利用 循环节 可以更快地得到答案.
性质:
字符串str由k个最小循环节s1组成,则它的相等循环同构数为k.
证明:
设相等循环同构数为p,我们可以利用循环节s1构造出k个相等的循环同构,于是p>=k;
下面证明p<=k:
如果p>k
假设在移动完s1到尾部前,出现了相同的循环同构串,不妨设此时移动的串为s2,
则利用 循环节性质2第1种情况的推理方法 可以知道s2为字符串的一个循环节
且s2的长度<s1,与s1为最小循环节的条件矛盾,因此p<=k.
于是p只能等于k.
拓展KMP
有很长一段时间单纯地以为拓展kmp只是kmp倒过来跑,后来发现很多问题其实无法转换成kmp解决,于是怒学了一下拓展kmp.
首先比较一下kmp和拓展kmp解决的问题:
kmp解决了求所有主串的前缀pre[i] (0<=i<n)的后缀与模式串前缀的最大匹配长度问题;
拓展kmp解决了所有主串的后缀suf[i]的前缀,与模式串前缀的最大匹配长度问题.
1 void getNext() { 2 int l = 1, r = -1, i, j; 3 4 for (next[0] = 0, i = 1; p[i]; ++i) { 5 if (i + next[i - l] - 1 < r) next[i] = next[i - l]; 6 else { 7 for (j = max(r - i + 1, 0); p[i + j] && p[i + j] == p[j]; ++j); 8 next[i] = j; l = i; r = i + j - 1; 9 } 10 } 11 next[0] = i; 12 }
设模式串为str;
定义next[i]为 str 与 它的后缀suf[i]的最大公共前缀长度.
r是当前已经确定匹配区间的最右端点,l是对应的左端点,即 r=l+next[l]-1;
当要求next[i]时
根据 next定义 str[ l , l+next[l]-1 ] == str[ 0 , next[l]-1 ];
得到 str[ i , l+next[l]-1 ] == str[ i-l , next[l]-1 ];
设s1=str[ i , l+next[l]-1 ];
讨论以下情况:
1. 若 i在 [l,r] 区间内
next[i-l]的值我们已经知道,这时候需要讨论:
如果 next[i-l] 小于 s1 的长度,那么可以知道在下标为 next[i-l] 的位置必定会失配,于是next[i]=next[i-l];
如果 next[i-l] 大于或等于 s1 的长度,那么直到r位置,我们都可以确定已经匹配上了,接下来需要确定r后面
位置的匹配情况,而此时i已经匹配了r-i+1的长度,next[i]从这个值开始计数就可以了,计数完成后i+next[i]-1
已经大于r,因此要更新 r=i+next[i]-1 , l=i ;
2.若 i不在[l,r]的区间内,即 i > r, 前面得到的信息无法用到,于是我们需要从头将str[i]与str[0]进行匹配,当然也要记得更新l,r.
复杂度:
2个循环变量i,j都是单调增的,而他们最多增加n次,因此 复杂度是线性的.
拓展kmp求循环节的方法参考kmp求循环节部分.
知道这些后可以来解决这个问题