ACM随笔 17.9.3 KMP

来源:互联网 发布:便宜又好看的淘宝店铺 编辑:程序博客网 时间:2024/06/06 16:26

今天晚上才看见群里公告,= =线段树没有看,不过这两天看了一下KMP算法,主要用来处理字符串的子串问题。

假设有字符串s1和字符串s2,长度分别为n和m。在求解s2是否为s1的子串这个问题中,称s1为匹配串,s2为模式串。KMP算法是一个复杂度为O(n+m)的算法,既可以求出字符串s2是不是字符串s1的子串,又可以求出字符串s2在字符串s1中出现了多少次,每一次出现在什么位置。

KMP算法首先用O(m)的复杂度求出一个next数组,记录了关于字符串s2的信息,然后用O(n)的复杂度快速求出字符串s2在字符串s1中的位置。

若next[j]==k,则k为满足以下条件的最大值;s2[0...k-1]与s2[j-k...j-1]相同,且s2[k]与s2[j]不同。若不存在这样的k,则next[k]=-1;


假设s1为匹配串,s2位模式串 ,next数组位失配指针。并且next已得到

i=j=0;

while(s1[i]!='\0'){

        if(j!=-1&&s2[j]=='\0'){

                  j=0;

                 }

         else{

                 while(j!=-1&&s1[i]!=s2[j])

                          j=next[j];

                  i++;

                  j++;

         }

}

//最后还要检查s2[j]=='\0',满足条件,则s1[i-m...i-1]等同于s2[0...m-1];

循环的每一步都判断s1[i-j...i]与s2[0...j]是否相等。

如果相等则应该寻找s1[i-j]开头最多能匹配到多少,即令i和j都加1;

如果不相等,应该将s2右移,next数组已经标记s2右移多少能保持s1[i-j...i]与s2[0...j]仍相等。

一直进行下去,当s2[j]=='\0‘时,就表示匹配成功了一个串。

下面来说一下next的求法。

 void cal_next(char *str, int *next, int len)
{
    next[0] = -1;//next[0]初始化为-1,-1表示不存在相同的最大前缀和最大后缀
    int k = -1;//k初始化为-1
    for (int q = 1; q <= len-1; q++)
    {
        while (k > -1 && str[k + 1] != str[q])//如果下一个不同,那么k就变成next[k],注意next[k]是小于k的,无论k取任何值。
        {
            k = next[k];//往前回溯
        }
        if (str[k + 1] == str[q])//如果相同,k++
        {
            k = k + 1;
        }
        next[q] = k;//这个是把算的k的值(就是相同的最大前缀和最大后缀长)赋给next[q]
    }
}

原创粉丝点击