KMP--next数组的理解
2015-08-07 21:16
232 查看
刚开始接触这个,网上都说next数组时KMP的精华,很多题都用到了next数组的性质,折腾了一天终于对next数组的来源、作用有了大致了解,看了网上很多讲解,最终在学长发的ppt的过程,茅塞顿开
,懂了一丢丢。
1.KMP算法的作用
KMP算法是用来处理字符串匹配的。换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串)。例如”Today is Tuesday”.中是否包含”day”,在哪些位置包含。
假如,A="abababaababacb",B="ababacb",我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,
A[i-j+ 1..i]与B[1..j]完全相等。(这句话很关键)
也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符
(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。
当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。
从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。
P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。(这句话很关键,这也就是俗称的next 数组的最初来源)
但有可能j到了0仍然不能满足A[i+1]=B[j+1],当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。
2.next数组的结论:
如果对于next数组中的 i, 符合 i % ( i - next[i] ) == 0 && next[i] != 0 , 则说明字符串循环,而且
循环节长度为: i - next[i]
循环次数为: i / ( i - next[i] )
(这个是别人总结的结论,数学思维好强大)
代码实现:
poj--seek the name,seek the fame(这便用到了上面特大字的类容)
,懂了一丢丢。
1.KMP算法的作用
KMP算法是用来处理字符串匹配的。换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串)。例如”Today is Tuesday”.中是否包含”day”,在哪些位置包含。
假如,A="abababaababacb",B="ababacb",我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,
A[i-j+ 1..i]与B[1..j]完全相等。(这句话很关键)
也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符
(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。
当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。
从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。
P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。(这句话很关键,这也就是俗称的next 数组的最初来源)
但有可能j到了0仍然不能满足A[i+1]=B[j+1],当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。
2.next数组的结论:
如果对于next数组中的 i, 符合 i % ( i - next[i] ) == 0 && next[i] != 0 , 则说明字符串循环,而且
循环节长度为: i - next[i]
循环次数为: i / ( i - next[i] )
(这个是别人总结的结论,数学思维好强大)
代码实现:
void getn() { int i=0; int j=-1; next[i]=j; while(i<l1) { if(j==-1||w[i]==w[j]) { i++; j++; next[i]=j; { if(next[i]==0) ans++; } } else j=next[j]; } }//ans=0开始,w为字符型数组
poj--seek the name,seek the fame(这便用到了上面特大字的类容)
#include<stdio.h> #include<string.h> #define M 400010 char a[M]; int per[M]; int l; int z[M]; void getp() { int i,j; i=0;j=-1; per[i]=j; while(i<l) { if(j==-1||a[i]==a[j]) { i++;j++; per[i]=j; } else j=per[j]; } } int main() { int i; while(scanf("%s",a)!=EOF) { l=strlen(a); getp(); z[0]=l; int k=1; for(i=l;i>=0;) { if(per[i]<=0)break; z[k++]=per[i]; i=per[i]; } for(i=k-1;i>=1;i--) printf("%d ",z[i]); printf("%d\n",z[i]); } return 0; }
相关文章推荐
- java线层的使用
- static 用法总结
- 二叉树(C语言)
- C++ 中的封装、继承
- HDU2.1.3 相遇周期
- 【HDU 4122】RMQ
- iOS 8中使用CoreLocation定位
- springmvc 下使用自定义注解获取登录信息
- 使用OpenCV玩家营造出一个视频控制(没有声音)
- Java线程:大总结
- CF One-Dimensional Battle Ships(set运用)
- (转)Java线程:大总结
- HDU 5353 Average(这里或许有你一直WA的原因)
- 二维数组中的查找
- 教会他人,让其成为你的接棒人
- Day1_HTML_表格
- HDOJ 1015 Safecracker(DFS)
- Java线程:新特征-原子量,障碍器
- 【数据结构】 CF 547B Mike and Feet
- Linux命令全解