字符串-kmp算法

kmp算法:给定长分别为n,m的字符串s,t,求t是否是s的子串。时间复杂度o(n+m)

算法理解:(默认m比n小)

s:s[0],s[1],s[2],……,s[n-1]

t:t[0],t[1],t[2],……,t[m-1]

1)先看一下朴素的字符串匹配:

若在k位置不匹配,即s[k]!=t[k],则t字符串向前移一位,继续比较。重复此过程,复杂度o(mn)

s:s[0],s[1],s[2],…,s[k],…,s[n-1]

t:t[0],t[1],t[2],…,t[k],…,t[m-1]

–>     t[0],t[1],……,t[m-1]

如此高的复杂度,ac是不可能的了,怎么优化呢?

注意到t字符串每次匹配不成功就向前移动一位,能不能多移动一些呢,如果能,那么最多能移动多少呢?

后文中的next[]会解决这个问题,若在第k处不匹配,则最多向前移动k-next[k]位。

2)下面引入kmp的核心思想,next[]数组:

意义:next[i]表示前i位(即:0,1,…,i-1)的最长公共前后缀长度

理解:公共前后缀,表示既是前缀,又是后缀。最长公共前后缀长度,即所有公共前后缀中长度最长的长度。

计算:采用递推的方法(显然next[0]=-1,next[1]=0)。

现在已知next[0],……,next[i],令k=next[i],求next[i+1]:

1.若T[k]==T[i],显然:next[i+1]=next[i]+1=k+1

2.若T[k]!=T[i],怎么办呢?其实下图完美的阐释了解决方案。

《字符串-kmp算法》

当T[k](就是T[next[i]])!=T[i],说明next[i+1]<=next[i],可以认为:

①.若T[next[next[i]]]==T[i],则next[i+1]等于next[next[i]]+1。

②.①条件不成立,则继续划分,比较next[next[next[i]]],直到可以。

3)匹配过程

在k处不匹配:若next[k]!=-1,则向前移动k-next[k]个位置,否则:

s:s[0],s[1],s[2],…,s[i],…,s[n-1]

t:t[0],t[1],t[2],…,t[k],…,t[m-1]

next[k]存在;t[0],t[1],…,t[next[k]],…,t[m-1]

不存在:t向前移动k+1位

s:s[0],s[1],s[2],…,s[i+1],…,s[n-1]

t:                  t[0],t[1],t[2],…,t[k],…,t[m-1]

4)next[]存在的合理性证明

假如在t字符串k位置不匹配,利用反证法证明。

1.先证明:next[k]存在时,t字符串最多向前移动k-next[k]位。

s:s[0],s[1],s[2],…,s[i],…,s[n-1]

t:t[0],t[1],t[2],…,t[k],…,t[m-1]

t:   …t[1],t[2],…,t[p],…,t[m-1]

假设t字符串向前移动x位时,可以与s字符串i位置之前匹配,且此时0<x<k-next[k]。

不妨令此刻与s字符串i位置比较的是p位置(p=k-x),则:t[p-1]=s[i-1],t[p-2]=s[i-2],……,t[0]=s[i-p]

由题又有:t[k-1]=s[i-1],t[k-2]=s[i-2],……,t[0]=s[i-k]

即:t[p-1]=t[k-1],……,t[0]=t[k-p],等价于t在k位置之前有一个长为p的公共前后缀。

而:p=k-x>next[k],与next[k]是最长公共前后缀长度矛盾。

所以:最多向前移动k-next[k]位

2.next[k]不存在时,应该向前移动k+1位。证明同一。

5)复杂度分析

利用摊还分析可以证明。

在求next[]时,if语句满足k才增加1,最好情况k增加m次,假如这m次全部用来减小,也至多减少m次,摊还到每次for循环中也就相当于一次,也就是说复杂度是o(m)。

在进行kmp分析时,i至多自加n次,复杂度o(n),j的复杂度同next[]复杂度,为o(m)。所以总复杂度为o(n+m)。

也就是o(n)。

*转一篇厉害的文章:http://www.matrix67.com/blog/archives/115

6)模板:

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;

typedef long long ll;
const int N=1e5+5;

int next[N];
void getnext(string t){
    next[0]=-1;
    int i=0,k=-1;
    while(i<t.size()){
        while(k>=0&&t[i]!=t[k]) k=next[k];
        k++,i++;
        if(t[i]==t[k]) next[i]=next[k];
        else next[i]=k;
    }
}
bool kmp(string s,string t){
    int i=0,j=0,l1=s.size(),l2=t.size();
    while(i<l1&&j<l2){
        if(j<0||s[i]==t[j]) i++,j++;
        else j=next[j];
    }
    return j==l2;
}
int main(){
    string s,p;     
    while(cin>>s>>p){
        getnext();
        cout<<((kmp(s,p))?"YES":"NO")<<endl;
    }
    return 0;
}

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