KMP算法的原理与实例

文章介绍了KMP算法,一种能降低字符串查找时间复杂度的算法。在最坏情况下,暴力匹配的时间复杂度为O(NM),而KMP算法通过构建next数组优化匹配过程,避免重复比较。文章详细阐述了next数组的构造方法,即利用递推法,并给出了查找函数的实现。此外,还提供了一道相关题目及解题代码示例。
摘要由CSDN通过智能技术生成

                                                

  校赛考了个这种算法,当时不懂KMP算法于是就光荣的TLE了,赛后看题解发现是没见过的算法,写篇题解纪念一下。

  KMP算法是一种能够大大降低字符串查找的时间复杂度的算法,正常来说,设被查找字符串(以下简称主串)的长度为n,需要匹配的字符串(以下简称)副串的长度为m,在最差情况下,对于0<=i<=n-m的主串里的每一个元素都需要查找m的长度,故时间复杂度为O(NM)。

                              

 

                                                 暴力解法图示

那么我们怎么想办法优化呢,可以发现,我们不必要每次每次都一步步往后走,每次失败后都可以直接跳过一部分不可能成立的序列,比如上图中间的第一步可以直接跳过,故我们可以创建一个数组,数组中每个位置储存副串位置相对于头前缀的长度,即如果失败直接从副串中next对应的位置开始比较。

  好了,现在问题来了,我们怎么构造这一个数组呢,考虑递推法:

  1. 对于除了零以外的任何元素x,如果next[0],next[1],next[2],next[3],…,next[x-1],已知,令temp=next[x-1],如果主串a[x]=a[temp],说明x是在temp的基础上的下一个元素,next[x]=temp+1;
  2. 如果配对失败,则说明其对应的前缀需要向前推到再上一个可能出现的位置,即temp=next[temp-1],一直到a[temp]=a[x],为止,

递推构造next数组的方法如下:其中ss为

nexttt[0]=0;
    unsigned long long int x=1;
    int temp=0;
    

    while(x<ss.length())
    {
                if(ss[temp]==ss[x])
        {
            

            nexttt[x]=temp+1;
            temp++;
            x++;
            
            
        
}
        else if(temp)
        {
            temp=nexttt[temp-1];
        }
        else
        {
            nexttt[x]=0;
            x++;
        }
    }

bbbcdd对应的next串为:012000

然后对应的查找函数:

int tar=0;//其在主串的位置
    int pos=0;//其在副串的位置
    while(tar<s[k].length())
    {
        if(s[k][tar]==ss[pos])
        {
            tar++;
            pos++;
        }
        else if(pos!=0)
        {
            pos=nexttt[pos-1];
        }
        else
        {
            tar++;
        }
        if(pos==ss.length())
        {
            flag=1;
        }
    }

最后再附上一道题:

样例:

 

5
3 19
iamdata juststructure notstring
datastructurestring
3 8
iiiiii llove yesxtu
ilovextu
3 3
not aurora st
nst
3 15
kids mother people
kidmotherpeople
3 6
bbbb bbbccc dddd
bbbcdd
 
输出结果:
4 9 6
1 4 3
1 0 2
3 6 6
0 4 2
Ac代码:

#include<iostream>
#include<string>
using namespace std;
string s[20];string s1;
int  nexttt[200000];
int ans[30];
int check(string ss,int k)
{

    int flag=0;
    nexttt[0]=0;
    unsigned long long int x=1;
    int temp=0;
    //cout<<"ssp= "<<ss<<endl;
    while(x<ss.length())
    {
        //cout<<"###"<<ss[temp]<<" "<<ss[x]<<" "<<temp<<" "<<x<<endl;
        if(ss[temp]==ss[x])
        {
            //cout<<"%%%%%"<<endl;
            nexttt[x]=temp+1;
            temp++;
            x++;
            //cout<<"XXXX= "<<x<<" "<<temp<<endl;
            
        
}
        else if(temp)
        {
            temp=nexttt[temp-1];
        }
        else
        {
            nexttt[x]=0;
            x++;
        }
    }
    for(int i=0;i<ss.length();i++)
    {
        cout<<"nexttt[i]= "<<nexttt[i]<<endl;
    }
    int tar=0;//其在主串的位置
    int pos=0;//其在副串的位置
    while(tar<s[k].length())
    {
        if(s[k][tar]==ss[pos])
        {
            tar++;
            pos++;
        }
        else if(pos!=0)
        {
            pos=nexttt[pos-1];
        }
        else
        {
            tar++;
        }
        if(pos==ss.length())
        {
            flag=1;
        }
    }
    return flag;
}

void erfeng(int left,int right,int mark,int k)
{

    while(left<=right)
    {
        int mid=(left+right)/2;
        string ss;
        for(int i=mark-mid;i<mark;i++)
        {
            ss+=s1[i];
        }
        //cout<<"ss= "<<ss<<endl;
        int temp=check(ss,k);
        if(temp==0)
        {
            
            
right=mid-1;
        }
        else
        {
            ans[k]=mid;
            left=mid+1;
        }
    }
}

int main()
{

    int t;
    cin>>t;
    while(t--)
    {
        int k,n;
        cin>>k>>n;
        for(int i=0;i<k;i++)
        {
            cin>>s[i];
        }
        
        
cin>>s1;int mark=n;
        for(int i=k-1;i>=0;i--)
        {
            
            
int tt=s[i].length();
            //cout<<"mark= "<<mark<<" "<<tt<<endl;
            erfeng(0,min(tt,mark),mark,i);    
            
mark-=ans[i];
        }
        for(int i=0;i<k;i++)
        {
            cout<<ans[i]<<" ";
        }
        cout<<endl;
    }
}


                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值