欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

KMP算法及其应用

程序员文章站 2022-06-04 10:51:44
...

前言

今天学习了一个新算法:KMP算法
其实很久以前学过早忘了
KMP算法是用于处理字串问题的算法。

参考Matrix67的博客:KMP算法详解|Matrix67

KMP算法的原理

假设有字符串A和B,要求判断B是否是A的字串
其实就是对于每个i,求最大的j,使得Aij+1iB1j一一匹配
能匹配j指针就往后跳一个
否则就需要往回退
如下图:
KMP算法及其应用
我们希望往回退得越少越好,
通过上图可以发现,其实最佳方案就是 最长相同前后缀
对字符串B的每个前缀字串,找一个最长的长度l,使得长度为l的前后缀相同
这个可以预处理出来,第i个前缀的长度l记为next[i]
那么每次不匹配就往回退next[j]即可
只要出现j==m的情况就说明B是A的字串

代码如下:

for (int i=1,j=0;i<=n;i++){
    while (j>0&&B[j+1]!=A[i]) j=nxt[j];
    if (B[j+1]==A[i]) j++;
    if (j==m){
        //do something……
    }
}

下面来说预处理的事。
怎样得到next[]?假设已经得到next[1~i-1],现在求next[i]
设next[i-1]为j,如果B[j+1]==B[i],那么next[i]=next[i-1]+1
否则需要缩小j的范围。
KMP算法及其应用
有读者可能会发现,寻找next[i]的过程其实就是将B[1~i-1]与B[]匹配的过程
所以回退next[j]即可

代码如下:

nxt[1]=0;
for (int i=2,j=0;i<=n;i++){
    while (j>0&&B[j+1]!=B[i]) j=nxt[j];
    if (B[j+1]==B[i]) j++;
    nxt[i]=j;
}

接下来证明复杂度。
通过上面的代码可以发现,j指针最多向后跳n次
而j在任何时候都是大于0的,所以回退也是O(n)级别的
所以KMP算法的复杂度是O(n)

KMP算法的应用

通过next[]数组,可以得到一个字符串的最小周期以及最小循环节
最小周期为n-next[n],如果周期能整除n,最小循环节就是周期
否则是n

相关标签: KMP