KMP算法(字符串匹配算法)

字符串匹配算法的一个天然的原始的算法就是设长度为m的pattern与之对应的text,text从第一个位置开始检测与pattern是否相等,如果遇到一个不相等text就向前移动一位,时间复杂度为n*m,但是KMP算法巧妙之处就在于每次遇到一个不相等时,会根据前缀函数(prefix function)来决定移动多少格,而不是每次一格,将时间复杂度优化到线性时间(n+m)。对于kmp算法的原理介绍,我建议去看算法导论,里面有着详细的讲解,里面还有其他几种的字符串匹配算法讲解,但是对于竞赛,目前需熟练掌握KMP。
《KMP算法(字符串匹配算法)》

我们现在定义s为一个text经过s次移动后与pattern匹配的合法位移(s可以为0)
《KMP算法(字符串匹配算法)》

现在介绍KMP ,这个算法需要对pattern进行一个预处理,计算一个出前缀函数,设为π(x)(算法导论用的即是这个符号)
描述:坐标从1到x的字符串里 前缀和后缀相等的最大长度,显然π(1)=0
《KMP算法(字符串匹配算法)》
这个前缀函数还有许多其他非常有用的应用,计算周期等
那么怎么计算这个前缀函数?现在我们用next来代替π
首先假设我们知道了next[x],如果P[next[x]+1]==P[x+1],那么下一个x+1的next值就是next[x+1]=next[x]+1,这个式子不证明,通过这个式子,我们便可以递推着去计算,但是如果P[next[x]+1]!=P[x+1]怎么办?
设k=next[x],我们在接下来判断P[next[k]+1]==P[x+1],如果相等next[x+1]=next[k]+1,如果不相等,重复上述步骤,直到k==0时终止,这个时候相当于去判断p[1]==p[x+1](即比较头和尾),如果还不相等则next[x+1]=0。
接下来是计算next函数的模板(注意是计算机从坐标0开始计算)

public static int[] next(int[] x)//x数组即为pattern
    {
        int next[]=new int[x.length];//每个坐标对应一个前缀函数值
        next[0]=0;//显然第一个就是等于0
        int k=0;//k值对应于i-1的前缀值,用来计算下一个i的前缀值
        for(int i=1;i<x.length;i++)
        {
            while(k>0&&x[k]!=x[i])//退出循环时k==0或者x[k]==x[i]
                k=next[k-1];
            if(x[k]==x[i])//判断是因为k==0还是因为x[k]==x[i]
                k++;
            next[i]=k;
        }
        return next;
    }

有了next数组 我们就知道该移多少格(想想为什么要前缀等于后缀?)
《KMP算法(字符串匹配算法)》
下面是KMP代码

    static void KMP(int P[],int T[])//假设这里的字符都是数字
    {
        int next[]=next(P);
        int p=0;//初始已匹配成功的字符个数
        for(int j=0;j<T.length;j++)
        {
            while(p>0&&P[p]!=T[j])//p是以匹配的个数即0到p-1,所以去判断下一个是否匹配的是P[p]而不是P[p+1]
                p=next[p-1];
            if(P[p]==T[j])p++;
            if(p==P.length)
            {
                System.out.println(j-p+1);//打印出有效s
                p=next[p-1];//如果题目要求只要一个匹配,这行代码可不需要,直接break即可
            }
        }
    }

可以发现KMP算法与求next的算法非常相似,结束,累死我
注:以上图片均来自算法导论,有兴趣可自行查阅

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/Coldfresh/article/details/67637942
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞