您的位置:首页 > 其它

字符串查找问题,求解字符串的周期(KMP算法)

2016-10-25 22:27 239 查看
本总结是是个人为防止遗忘而作,不得转载和商用。

题目

         给定文本串text和模式串pattern,从text中找出pattern第一次出现的位置。

         记:文本串长度为N,模式串长度为M

暴力求解(BF)

         这个简单。

                   1,i = 0, j = 0

                   2,判断test[i +j] 是否等于 pattern[j]

                            相等则j++后继续回到第二步

                            不相等则j = 0,i++,直到遍历完test

         因此,该算法的时间复杂度O(M*N),空间复杂度O(1)

KMP算法

         KMP算法是一种线性时间复杂度的字符串匹配算法,它是对BF算法改进。

Tip:

KMP算法的时间复杂度O(M+N),空间复杂度O(M)

         改进在哪里呢?

         BF算法在匹配失败时,要重新从pattern的第一个字母开始匹配,而KMP对这点做了改进。

         为了解释这点,我们假设现在的情况是:当前字符匹配成功,即text[i]==pattern[j],令j++,继续匹配下一个字符时发现匹配失败,即:text[i] ≠pattern[j],如下图:

                  


                   上图的第一行:文本串

                   上图的第二行:模式串

                   我们的目的是查找模式串在文本串中第一次出现的位置,而当前的状态是:文本串和模式串中土黄色的部分已经完全一样,但文本串中黄色部分的字符T[i]不等于模式串中绿色部分的字符P[j],即:T[i+j] != P[j]。

         接下来,我们做这样的事情:文本串保持不动,模式串向后滑动一段位置。这样或许能够更快的找到模式串在文本串中的位置,如下图:

                  


         那么滑动多少呢?为了方便说明,这里把上图的第二行和第三行画成下面的样子:

                  


         上图第1行的A~B就是上上图第二行的土黄色部分,d就是P[j],而这么滑动的条件就是A=B。

         可以这么做的原因是:因为A= B,所以滑动之后我只需要从pattern的c开始和test[i]进行对比就好(假设),不用从头对比了。

         总之:为了能向后滑动模式串,我们需要在模式串的P[j] 前找到一个最长的前缀A和最长的后缀B,且A = B。然后我只需要对比下c和test[i]是否相等就可以,A那一块就不用再对比了。

         总之:查找满足条件的最大的k,使得:p0p1 ...pk-2pk-1= pj-kpj-k+1 ...pj-2pj-1

Next数组

         


         假设在c处模式串不等于文本串,于是就找出c前面的最长且相等的前后缀,方法很简单,如图所示对比所有的前后缀,找出相等且最长的。

                   PS:abaab和自身是一定相等的,没意义,pass。

         因为找到的最长相等前后缀的长度为2,所以next数组的c的位置就赋值为2。

                   PS:笔试面试中有可能只需要求next数组。

如何求Next数组

         于是,对于模式串的位置j,有next[j]=k,即:

                   p0p1 ...pk-2pk-1= pj-kpj-k+1 ...pj-2pj-1

         则,对于模式串的位臵j+1,考察pj:

                   若p[k]==p[j]:则next[j+1]=next[j]+1

                   若p[k]≠p[j]:则看下图

                  


                  上图的黄色不等于灰色表示p[k]≠p[j]。

                  这时怎么办呢?

                  你看,虽然B那一块等于A那一块,但p[k]≠p[j],所以B那一块加上灰色就不等于A那一块加上黄色,那我向前考察。

                  对于k来说,其next[k] 已经求出来了,假设next[k]= h,这意味着k前面那一段(B那一块)有个长度为k的前缀和后缀内容一样,即1那一块 = 3那一块。

                  又因为B = A,所以1那一块 = 2那一块。

                  因此对j来说,它前面那一大段有个长度为h的前缀和后缀是相等的。

                  于是,既然“B+k”不等于“A+j”,他俩没戏了,那我看看“1+蓝色”是否等于“2+黄色”?

                           如果相等,则next[j+1]= h+1

                           如果不相等,那就重复上面的过程,看看next[h]。

代码

         综上,计算Next数组的代码如下:

                   voidGetNext(char *p, int next[])

                   {

                            intnLen = (int)strlen(p);

                            next[0]= -1;

                            intk = -1;

                            intj = 0;

                            while(j < nLen - 1 ){

                                     //此刻,k即next[j-1],且p[k]表示前缀,p[j]表示后缀

                                     //注:k == -1表示未找到k前缀与k后缀相等,首次分析可先忽略

                                     if( k == -1 || p[j] == p[k] ) {

                                               ++j;

                                               ++k;

                                               next[j]= k;

                                     }else { // p[j] 与 p[k] 失配,继续递归计算前缀p[next[k]]

                                               k= next[k];

                                     }

                            }

                   }

进一步分析Next数组

         文本串匹配到i,模式串匹配到j,此刻,若text[i]≠pattern[j],即失配的情况:

                  若next[j]=k,说明模式串应该从j滑动到k位置;

                  但若此时满足pattern[j]==pattern[k],因为text[i]≠pattern[j],所以text[i]≠pattern[k],即i和k没有匹配,应该继续滑动到next[k]。

                  换句话说:在原始的next数组中,若next[j]=k并且pattern[j]==pattern[k],next[j]可以直接等于next[k],即:由滑动到k位置改为直接滑动到next[k]位置。

变种的next数组

         于是,我们可以在做另一种next数组,如下:

                  


         以最后的a举例,之前得出的值使原始next中所示的值P[8]=2,但是在改进之后,因为T[P[8]]= T[8],所以P[8] = P[2],而P[2] 在求得时候同理它是等于p[0] 的,所以新next里p[8] = p[2] = p[0] = -1。

         PS:如果模式串以1开始数,则原始next和新next中的所有值都要加1。因此在面试时需要先问主考官:你问我的KMP是不是改进版本的KMP,模式串是从0开始数的还是从1开始数的。

KMP可以求解字符串的周期

         


         如T[12]的next[12] = 9,而T[12]的12 - 9 = 3,3是可以被12整除的,所以3就是T的最小周期长度。

                   PS:这里的next串是原始next。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 kmp
相关文章推荐