KMP算法

浏览过很多的博客,发现主要有两种的求next数组的方法:

第一种:F数组(也就是next数组)表示到第i个字符所构成的最长前后缀的长度减一,这样得到的next数组里面的数也是模板串的下标。

int main()
{
    char B[100];
    gets(B);
    int m=strlen(B);
    int F[100]={-1};
        for (int i=1;i<m;i++)
    {
        int j=F[i-1];//j指B数组的下标
        while ((B[j+1]!=B[i])&&(j>=0))
            j=F[j];
        if (B[j+1]==B[i])
            F[i]=j+1;//F[i]=F[j]+1;
        else
            F[i]=-1;
    }
    //GetNext(B,F);
    for(int i=0;i<m;i++)
        cout<<left<<setw(3)<<F[i];
    cout<<endl;
    return 0;
}

下面是较多出现的

void GetNext(char* p,int next[])
	{
	    int pLen = strlen(p);
	    next[0] = -1;
	    int k = -1;
	    int j = 0;
	    while (j < pLen )
	    {
	        //p[k]表示前缀,p[j]表示后缀
	        if (k == -1 || p[j] == p[k])
	        {
	            ++k;
	            ++j;
//	            if(p[j]!=p[k])
            next[j] = k;
//            else
//                next[j]=next[k];
	        }
	        else
	        {
	            k = next[k];
	        }
	    }
	}

先说一下第二种求next数组,优化过和不优化的next数组值是不同的比如abcac,得到就不同;但是next数组里面存的都是在失配后需要倒回的下标。第一种得到的next是最长前后缀的长度减去1,两者的next数组含义是不同的,因此得到的数组也就是不同的。推荐第二种优化的;

下面是求kmp的模板:

int KMP(char *str, int slen, char *ptr, int plen)
{
    int *next = new int[plen];
    cal_next(ptr, next, plen);//计算next数组
    int k = -1;
    for (int i = 0; i < slen; i++)
    {
        while (k >-1&& ptr[k + 1] != str[i])//ptr和str不匹配,且k>-1(表示ptr和str有部分匹配)
            k = next[k];//往前回溯
        if (ptr[k + 1] == str[i])
            k = k + 1;
        if (k == plen-1)//说明k移动到ptr的最末端
        {
            //cout << "在位置" << i-plen+1<< endl;
            //k = -1;//重新初始化,寻找下一个
            //i = i - plen + 1;//i定位到该位置,外层for循环i++可以继续找下一个(这里默认存在两个匹配字符串可以部分重叠),感谢评论中同学指出错误。
            return i-plen+1;//返回相应的位置
        }
    }
    return -1;  
}

两种方法得到的结果是一样的,但其中都有一个共同点,就是当其自身匹配自身不相等的情况时,都有一句k=next[k](第一种是F数组),为什么?链接有解释,包括为什么next用-1开头,为甚用前后缀求:还有优化第二种next求法及其证明

第一种求next

kmp算法是将一段字符串分成子串进行求最长前后缀的,因此他有一个性质,就是做这道题传送门

有了kmp算法的next数组(保存某段子串的相同前后缀的最大长度,不包括自身),可以从最长的相同前后缀开始考虑,对于串S,根据next[strlen(S)-1]得到的最长相同前后缀为S1,则再根据next[strlen(S1)-1]得到的S1的最长相同前后缀S2肯定也为S的相同前后缀。(通过画图可以很容易的证明)。 
    这样,通过不断的向前查找next数组,得到一系列的相同前后缀。下面证明这些前后缀包含了所有的相同前后缀: 
        由于我们得到的是一系列最长相同前后缀,假设有一个相同的前后缀Sk不属于这些通过next递推得到的前后缀集合,那么len(Sk)必定小于len(S1),且Sk必定为S1的子串(通过画图很容易看出来);此时若Sk是S1的最长前后缀,则与前提Sk不为某一子串的最长前后缀矛盾,那么Sk不是S1的最长前后缀,则可以退出len(Sk)必定小于S2,且Sk必定为S2的相同前后缀..... 数学归纳递推下去,可以知道Sk要么为某个Si的最长前后缀,要么len(Sk) = 0. 这样可以证明这些通过next数组递推得到的前后缀包含了所有的相同前后缀

举个例子

字符串:ababcababababcabab



      1、长度为 18 时,字符串为 ababcababababcabab,前缀 ababcabab,后缀 ababcabab,输出18; 
      2、长度为 9 时,字符串为 ababcabab,前缀 abab,后缀abab,输出 9; 
      3、长度为 4 时,字符串为 abab,前缀 ab,后缀 ab,输出 4;
      4、长度为 2 时,字符串为 ab,前缀 a,后缀 b,此时前后缀不相同,输出此时的字符串长度 2,并结束;
      (因为字符串是从后向前一一查找判断的,所以当找到不想同的前后缀时既查找完毕,输出并结束) 
      ......
      如果还不明白,我就仍然以上述例子说,next[18]=9;意思是说,此字符串有长度为 9 的相同前后缀, 如上 1 所示,
      设字符串长度为 9,此时next[9]=4,表示在此长度为 9 的前缀中,又有长度为 4 的相同的前后缀,
      设字符串长度为 4,此时next[4]=2;.............
      依次进行.... 

https://blog.csdn.net/liu940204/article/details/51333045转载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值