字符串KMP算法小结
做了几道KMP的简单题,有些需要记下来的关键点,以供以后查看。
先是KMP的模板:
const int maxn = 1000000+10; char s1[maxn],s2[maxn]; int len1,len2,next[maxn]; void getnext() { int i=0,j=-1; next[0]=-1; while(i<len2) { if(j==-1||s2[i]==s2[j]) { i++,j++; next[i]=j; } else j=next[j]; } } int kmp() { int i=0,j=0,sum=0; while(i<len1&&j<len2) { if(j==-1||s1[i]==s2[j]) i++,j++; else j=next[j]; if(j==len2) { sum++; j=next[j]; } } return sum; }
1.其中要先把数组放在外面做全局变量,如果遇到数字的话,就把数组类型改成int 型,长度也放在外面,数组大小的话看题意;
2.s1[maxn]为主串,s2[maxn]为模式串,函数getnext();得到的next[maxn]是只关于模式串的s2[maxn],所以是s2[i]==s2[j];
3.对于不同的题:(实际上要改的就是kmp函数中后面关于j的。)
(1)要求找相同的,例如:abcd主串,ab模板串,sum会为1,所以主函数就if(kmp())printf("YES\n");
例子:HDU2203 解题报告:http://www.cnblogs.com/wgl20130409/p/3258393.html
(2)要求找多少个,可以重叠,例如:azazaza主串,zaz模板串,有3个,就用上面的就行,直接输出sum就行;
例子:HDU1686 解题报告:http://www.cnblogs.com/wgl20130409/p/3264798.html
(3)要求找多少个,但不可以重叠,就把上面kmp函数中后一个j=next[j],改为j=next[j]+j;就行;
例子:HDU2087 解题报告:http://www.cnblogs.com/wgl20130409/p/3258261.html
(4)要求找到一个,输出首个的位置(使得位置最小就是第几个),这样的话,就不用sum了,利用j;在kmp 函数循环完,看j的大小,如果小于len2,就没有,反之 返回i-len2+1.
例子:HDU1711 解题报告:http://www.cnblogs.com/wgl20130409/p/3264761.html
差不多就是这样,万变不离其宗,不理解不要紧,只要记住模板,时间长了就慢慢明白。。。。。
加油!!!!!!!!!
今天上午又看了一上午的KMP,总算是把next[]函数弄懂了,但怎么和代码结合起来,还需要长久的“悟”才行。。
一般KMP有两种,我上面的代码next[]是代码比较简单的,但效率比另一种要低。
next[i]=x表示在第i+1个元素的时候他前面的有没有相同的,例子:ababc,对于第5个元素c来说,它的前两项与开始两项相同,所以next[4]=2;这实际上有什么作用呢??
看代码:if(j==-1||s1[i]==s2[j])
i++,j++;
else j=next[j];
简单说,就是 主串为aabaabxxxxx,模板串为aabaaa,原始的比较是一个一个比,如果不匹配就向后挪一个,但如果在模式串中有重复的就会浪费时间,如上当比到
aabaabxxxxx
aabaaa
012345
第6个不一样,说明第6个前面的两个aa和主串上的对应位置一样,而模板串中第6个前面的2个和开始2个一样,那么就可以直接让开始跳过去:
aabaabxxxxx
aabaaa 这就是next[5]=2;的作用;
那么怎么得到next[]函数呢??就是看模板串。
012345678
例子:ababcaabc 设next[0]=-1;然后看第2个元素,(实际上是第i+1个),定义j=0,j<i;为元素i之前的j个元素,然后让元素i之前的j个元素和开始j个元素比较是否相同,直到不同,next[i]=j;所以得到-1 0 0 1 2 0 1 1 2
今天下午又给同学讲了一下,又有新的理解。就是关于怎么实现的上面的思想。确切的说是怎么理解代码。
关于next[]的函数getnext()中if(j==-1||s2[i]==s2[j])的条件,j==-1是初始化进的条件,也是当连续与前面不相等的情况下让进的条件,例子:abcabdaxxxx,当后面的xxxx时,j就等于-1;
然后是其中的next[i]=j;因为比如说上面的例子,当i等于5时,基于理论,此时应该j=2;而j在本函数中有两个作用,一个是计数,一个是走位置比较(就是当做下标);
对于理解getnext()函数做的输出调试;
void getnext() { int i=0,j=-1; next[0]=-1; while(i<len2) { if(j==-1||s2[i]==s2[j]) { i++,j++; next[i]=j; printf(" %d %d ",i,j); //输出的就是i,j的位置;
} else { j=next[j]; printf("\n"); printf("** %d %d **",i,j); } //输出的i是下标,j的位置
} }

浙公网安备 33010602011771号