KMP算法简单应用


K(看)M(毛)P(片)算法最常用在字符串匹配。给定一个长的字符串(target string)和一个短的字符串(pattern string),要求判断pattern string是否是target string的子串,如果是,则返回子串的首个字符的下标;如果否,则返回-1。

解决这个问题最常想到的办法就是brutal force,即从target string第一个字符开始与pattern string比较,如果相等则比较target string和pattern string的下一个字符,如果不等则返回到target string中相等的字符的下一个字符。换句话说,假设我们用target和pattern分别表示两个字符串的指针,那么每一次比较不管两个string匹配到何种程度,只要不是完全匹配(即匹配完成),那么target永远只能增加1,这个算法的复杂度为O(mn).(m=strlen(target string),n=strlen(pattern string)).

以下是这个算法的C代码。

 1 int strstr(char *target, char *pattern)
 2 {
 3   int i,j;
 4   for(i=0;;i++)
 5     {
 6         for(j=0;;j++)
 7         {
 8             if(pattern[j]==0)return i;
 9             if(target[i+j]==0)return -1;
10             if(target[i+j]!=pattern[j])break;
11         }
12     }
13 }

导致这个算法时间复杂的关键,在于我们每次只能将target指针加一,而不能充分利用之前已匹配部分的信息。一个很好的例子由http://kenby.iteye.com/blog/1025599 给出,事实上我们可以通过利用已匹配部分的信息,让每次比较失败后target跳过多个位置。

下面给出KMP的代码再给出一些解释。

 1 int* overlay(char *str)
 2 {
 3     int *a,len=strlen(str),i,index;
 4     a[0]=-1;//美好的约定
 5     a=(int *)calloc(len,sizeof(int));
 6     for(i=1;i<len;i++)
 7     {
 8         index=a[i-1];//上一个
 9         while(index>=0&&a[index+1]!=a[i])
10         {
11             index=a[index];
12         }
13         if(a[index+1]==a[i])
14         {
15             a[i]=index+1;
16         }
17         else a[i]=-1;
18     }
19     return a;
20 }
21 
22 int strstr(char *target,char *pattern)
23 {
24     int i,j,*a,len1=strlen(target),len2=strlen(pattern);
25     a=overlay(pattern);
26     for(i=0,j=0;i<len1&&j<len2;)
27     {
28         if(target[i]==pattern[j])
29         {
30             i++;
31             j++;
32         }
33         else if(j==0)i++;//若第一个字符就不相等,则对target的指针加1即可
34         else j=a[j-1]+1;
35     }
36     if(j==len2)return i-j;
37     else return -1;
38 }

overlay函数是用递推求出pattern串每一个位置对应的覆盖值,算法在http://blog.csdn.net/power721/article/details/6132380有解释,不赘述。覆盖值数组的含义在上述博客中没有解释得很清楚,我举个例子。

首先假设这个字符串名为str。首字符的覆盖值是-1,这是约定。从第二个字符开始,我们看到b的覆盖值是-1,什么意思呢,意思就是在b之前的子串(包括b),不存在k满足str[0]str[1]...str[k-1]str[k]=str[1-k]str[2-k]...str[1]。接下来,第三个字符a的覆盖值为0,表示在a之前的子串(包括a),存在k=0使得str[0]str[1]...str[k-1]str[k]=str[2-k]str[3-k]...str[2]。第五个字符b的覆盖值为1,表示在b之前的子串(包括b),存在k=1使得str[0]str[1]...str[k-1]str[k]=str[4-k]str[5-k]...str[4]。

至于overlay函数的递推求法,博客里有说明递推过程,我也是看了好一会才看清楚orz...下面配个图,图里中括号里面是相同的字符串,小括号里面也是相等的字符串,以此第三次,第四次一直往下找...大家才思敏捷,一定可以看得懂。

 

其余的东西在以上两篇博客都讲得很清楚,我也不再赘述。算法导论里有关于KMP算法摊还分析的一些证明,在不同的地方,上文所述的覆盖值的含义有可能有一点点差别,命名也有不同,但总的意思和算法都是差不多的。

部分代码参考了https://leetcode.com/problems/implement-strstr/?tab=Description

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM