zoukankan      html  css  js  c++  java
  • KMP算法之next函数解释(大量的反证法 和数学归纳法来袭)

    先放get_nextval()函数的代码

    void get_nextval(const char str[],int *net)
    {
        net[0]=-1;
        int j=0,k=-1,len;
        len=strlen(str);
        while(j<len)
        {
            if(k==-1||str[j]==str[k])
                net[++j]=++k;
            else
                k=net[k];
        }
    }//在这个函数内数组下标是0开始的 

    在这个函数内数组下标是0开始的 

    首先你要明白next数组的含义

    next[j]的意思就是在下标j之前的字符串上  得出该字符串大的最大匹配数

    就像这样

     要求k最大即匹配数最多(两条黑线内的对应相等)(图不好先凑合一下 有时间找大牛给补下图)

    好的 现在咱们开始分析get_next函数

    next[0]=-1(代表没有最大匹配 应该将i指针右移动)

    假设next[j]=k成立  且0<=k<j (k!=j保证最大匹配数不为自己本身)

    1.如果str[j]==str[k]的话str[j+1]=k+1

              因为如果还存在str[j+1]>k+1的话即 str[j]>k  这与假设不成立    所以 str[j+1]=k+1

    2.如果str[j]!=str[k]的话 

              那就一直找k=next[k]直至 str[j]=str[k]  然后令str[j+1]=k+1

    也许很多人对第二个条件不太明白,那么下面咱们来证明第二个条件的正确性,

    刚开始我一直有一个疑问 为什么next[j+1]的值一定会通过这个方法找出来呢  万一存在一个更长的字符串与之匹配呢

    好的  现在来看一下图  , 假设我们通过第二个条件找到答案是k3即str[j]==str[k3] ,  但存在一个k'没有通过第二个条件找到(且k'>k3 要求k'更长)使得 str[0]......str[k']==str[j-k']......str[j](前k'+1个字符相等) 即next[j+1]=k'+1   

    此时k'>k3  且k'没有被第二个条件所找到

    因为k3<k'<j    那么 k'的长度一定在找到的k之间

    假设在k'的长度在k1和k2之间 且k2<k'<k1,那么k1判断之后发现str[j]!=str[k1],下一步k2=next[k1], 但是因为k2<k'<k1,即k2并不是k1的最大匹配, k'的匹配比k2更大 next[k1]的值应该是k';

    与假设next[k1]==k2矛盾 

    故不存在一个没有找到过的k'   使得k'>k3且 str[0]......str[k']==str[j-k']......str[j]str[j]==str[k']

    所以next[j+1]的值肯定能从第二个条件找到。

    get_nextval()函数还有一点可能有人有疑问,为什么当k=-1时 也执行next[++j]=++k

    因为当k==-1时 必定有过一次k=0且str[j]!=str[0]   即没有   以0为下标的字串 与  j之前的字符串 匹配 返回-1 此时因该让next[j+1]=0

    数学归纳法

    初始条件:next[0]=-1 符合题意  

    假设当n<=j    时候next[j]符合题意 

    又因为当n=j+1 时 由next[j+1]也符合题意

    所以算法正确

    /*
    字符串kmp
    */
    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=1010;
    void get_nextval(const char str[],int *net)
    {
        net[0]=-1;
        int j=0,k=-1,len;
        len=strlen(str);
        while(j<len)
        {
            if(k==-1||str[j]==str[k])
                net[++j]=++k;
            else
                k=net[k];
        }
    }
    int kmp(const char ss[],const char tt[])//参数传两个主川和模式串  返回值-1或者在主串中匹配的位置
    {
        int *net=new int[maxn];
        int ls,lt,i=0,j=0;
        ls=strlen(ss);
        lt=strlen(tt);
        get_nextval(tt,net);
        while(i<ls&&j<lt&&(ls-i)>=(lt-j))
        {
            if(ss[i]==tt[j])
            {
                i++;
                j++;
            }
            else//不匹配
            {
                int k=net[j];
                if(k==-1)
                {
                    i++;
                    j=0;
                }
                else
                    j=k;
            }
        }
        delete []net;
        if(j>=lt)
            return (i-j+1);
        return -1;
    }
    int main()
    {
        char ss[maxn],tt[maxn];
        while(~scanf("%s %s",ss,tt))
        {
            int ans=kmp(ss,tt);
            if(ans==-1)
                cout<<"未匹配"<<endl;
            else
            {
                cout<<"从主串的第 "<<ans<<" 个存在匹配"<<endl;
            }
        }
    }
  • 相关阅读:
    oracle查询表最后的操作时间
    设置tomcat开机自启
    jmeter 连接mysql
    ubuntu卸载软件
    转 ubuntu 安装chrome 和chromedriver
    转 ps -ef ps -aux 区别
    ubuntu 20 jenkins 开机启动
    Ubuntu20.04安装JDK
    ubuntu 安装指定版本gitlab
    Gitlab备份和恢复操作记录 转
  • 原文地址:https://www.cnblogs.com/dchnzlh/p/9780040.html
Copyright © 2011-2022 走看看