KMP算法中的next函数的证明

时间:2021-10-04 18:50:07

KMP算法的next函数的证明

1.1      Next函数定义与代码

1.1.1       定义

KMP算法中的next函数的证明

1.1.2       实现代码

/*代码摘自《大话数据结构》*/

/*t为字串,nextint数组,存储next*/

void getnext(string T,int *next)
{
       int i,j;
       i=1;j=0;next[1]=0;

       while(i<T[0])   //T[0]表示字符串T的长度
       {
              if(j==0 || T[i]==T[j])
              {
                     ++i;
                     ++j;
                     next[i]=j;
              }
              else
                     j=next[j];
       }
}

 

1.2      证明思路

采取分段证明的办法,证明j往区间(m,k)移动是不正确的,验证移动到m(next[j]=m)的正确性。

在证明的过程中,都是假设j移动到某个位置之后,讨论if判断条件会成立。因为如果是不成立的话,程序流程又会转移到j的移动,这样我们就会陷入死循环。而且,这种假设,即if成立,在逻辑上也是成立的,就像递归函数一样,只要一层是正确的,递归的肯定也没问题。

1.3      证明

首先观察这个函数的代码,我们可以获得如下信息:

(1)      若if成立,则i、j都加1;

(2)      i的值为k,则next[k]已经有了结果,且在下一轮if之前j= next[k];

(3)      当要求next[k]的时候,if中的判断条件是i=k-1;

 

 

证明:

前提条件:假设next[k]=s,

             即T[1]…[s-1]=T[k-s+1]…[k-1](1)

现求next[k+1]=?根据前面的结论,此时i=k,j=s。令next[s]=m.

现进行if判断:

(1)   若条件成立,

即T[i]=T[j],也即T[k]=T[s],结合式(1),则有

             T[1]…[s-1][s]=T[k-s+1]…[k-1][k]   (2)

        则next[k+1]=s+1,即代码中的next[i]=j;继续下一次if判断。

(2)   若条件不成立,

即T[i] != T[j],则我们需要移动j。

假设向区间 (s , k) 移动,假使使j=s+1,满足if条件,即T[k]=T[s+1],

根据代码,则next[k+1]=s+2,意味着

   T[1]…[s-1][s][s+1]=T[k-s]…[k-1][k]   (3)

等式两边去掉末位,

       T[1]…[s-1][s] =T[k-s]…[k-1]   (4)

从这可以推出next[k]=s+1,与前面矛盾,所以j不能向前移(假若向前移动更多位的话,就会推出next[k]比s+1更大的值)

 

             下面证明j向(m,s)区间移动是不行的。

             已知条件:i=k,j=s,next(s)=m,next(k)=s,T[k]!=T[s],需证明移动到m+1是不行的。

             现我们假设把j移动到m+1,而不是代码中的m。

             根据已知条件可以得到:

             next(k)=s =>T[1]…[s-1]=T[k-s+1]…[k-1]   (5)

             next(s)=m =>T[1]…[m-1]=T[s-m+1]…[s-1]   (6)

             if判断成立,T[k]=T[m+1],则得到next(k+1)=m+2,

                   T[1]…[m+1]=T[k-m]…[k]   (7)

                   两边去掉右边一位,则有T[1]…[m] =T[k-m]…[k-1]  (8)

                   观察(5),因为s>=m+1,所以k-m>=k-s+1,

                   所以T[1]…[m] =T[k-m]…[k-1]

                                   = T[s-m]…[s-1]  (由5)

                   从这里可以推出,next(s)至少为m+1,与已知矛盾。

                   显然,若移动到m+2等就可以得到更大的next(s)的值了。

 

接下来,我们证明移动j到next[j]的正确性。

设next[j]=m,此时j=s,next[s]=m,移动j到next[s]后,即j=m。

由next[s]=m有:

T[1]…[m-1] =T[s-m+1]…[s-1]   (9)

判断T[i]==T[j],即T[k]==T[m],条件成立之后,因为s>=m+1,所以

k-m+1>k-s+1,即T[k-m+1]在T[k-s+1]…[k-1][k]中。

      取(1)中等式两边右边的m-1项,得到:

                    T[s-m+1]…[s-1] =T[(k-1)-(m-1)+1]…[k-1]= T[k-m+1]…[k-1]  (10)

又结合(9)(10)有

                    T[1]…[m-1] =T[k-m+1]…[k-1]   (11)

      结合T[k]==T[m],有

                    T[1]…[m-1][m]=T[k-m+1]…[k-1][k]   (12)

                         从而next[k+1]=m+1,即代码中的next[i]=j。由next的定义可知,j向(0,m)区间移动的话就得不到正确的next值,应为已经找到了j=m满足要求,j值只能增大,减少就不符合定义。

 

至此,我们证明了把j回溯到大于m的任何位置都会得到错误的结论,且验证了移动到m可以得到正确的结论。