KMP--作用、模板、题单总结(包含题解)

本文深入探讨了KMP算法在处理字符串模式匹配中的应用,包括计算Next数组、查找循环串、判断子串重复等。通过实例解析算法细节,并给出多个相关编程题目的解决方案。

模板

#include <cstdio>
#include <cstring>
using namespace std;
int n;
char ts[100];
char ms[100];
int next[100];
int len;
 
void getNext()
{
    int i = 0, j = -1;
    next[0] = -1;
    while (i != len){
        if (j == -1 || ts[i] == ts[j]){
            next[++i] = ++j;
        }
        else
            j = next[j];
    }
}
int KmpMatch(char ms[], char ts[])
{
    int tlen = strlen(ts);
    for(int i = 0, j = 0; ms[i];)
    {
        if(j == -1 || ms[i] == ts[j])
        {
            if(j == tlen - 1)
                return i - tlen + 1;
            else 
                i++, j++;
        }
        else 
            j = next[j];
    }
    return -1;
}

Next数组的各种操作

  • 求最小循环节

看案例意会即可

if (len % (len - nxt[len]) == 0)
	printf("%d\n", len - nxt[len]);

2
abcabcabcabc
-1000123456789
  • next数组的规格

能求到next[n]这一位。

  • 在一个字母序列中,求有没有字母序列在前缀、后缀以及中间都出现。

既然要求三个相同的子串,而且有两个还必须在开头和结尾,那就求Next数组,Next数组存的是前后缀相同的长度,所以只需要找【2*i,L-Next[i]】之间相同的字串,即Next[j]==i;

  • 找循环串的每一个结束

    while(nex[len] != 0)
    {
    	q.push(nex[len]);
    	len = nex[len];
    }
    while(!q.empty())
    {
    	printf("%d ",q.top());
    	q.pop();
    }
    
  • 补全循环串的最小个数

    (len -nex[len]) -len %(len-nex[len])
    

KmpMatch函数里的各种操作

  • 求有多少个可分开的子串

    int KmpMatch(char ms[],char ts[])
    {
        BuildNext( ts);
        int tlen = strlen(ts);
        for(int i = 0,j = 0; ms[i]; )
        {
            if(j == -1 || ms[i] == ts[j])
            {
            	//这一段是关键
                if(j == tlen-1 )
                {
                   con++;
                   j = 0;
                   i++;
                }
                else
                    i++,j++;
            }
            else
                j  = nex[j];
        }
        return -1;
    }
    
  • 求有多少个可以重复的子串

    int KmpMatch(char ms[],char ts[])
    {
        int cnt = 0;
        int tlen = strlen(ts);
        for(int i = 0,j = 0; ms[i]; )
        {
            if(j == -1 || ms[i] == ts[j])
            {
                i++;
                j++;
                if(j == tlen)
                {
                    //这里是关键
                    j = nex[j];
                    cnt ++;
                }
            }
            else j = nex[j];
        }
        return cnt;
    }
    

例题

Uva 445 Periodic Strings

#include <cstdio>
#include<iostream>
#include <cstring>
using namespace std;
char str[100];
int nxt[100];
int len;
void getNext()
{
    int i = 0, j = -1;
    nxt[0] = -1;
    while (i != len){
        if (j == -1 || str[i] == str[j]){
            nxt[++i] = ++j;
        }
        else
            j = nxt[j];
    }
}
int main()
{
    scanf("%d", &n);
    while (n--){
        scanf("%s", str);
        len = strlen(str);
        getNext();
        if (len % (len - nxt[len]) == 0)
            printf("%d\n", len - nxt[len]);
        else
            printf("%d\n", len);
        if (n)
            printf("\n");
    }
    return 0;
}

hdu 剪花布条

#include<cstdio>
#include<cstring>
using namespace std;

int nex[100000];
int con;
void BuildNext(char ts[])
{
    nex[0] = -1;
    for(int i = 0, j = -1;ts[i]; )
    {
        if(j == -1 || ts[i] == ts[j])
            nex[++i] = ++j;

        else j = nex[j];
    }
}

int KmpMatch(char ms[],char ts[])
{
    BuildNext( ts);
    int tlen = strlen(ts);
    for(int i = 0,j = 0; ms[i]; )
    {
        if(j == -1 || ms[i] == ts[j])
        {
            if(j == tlen-1 )
            {
               con++;
               j = 0;
               i++;
            }
            else
                i++,j++;
        }
        else
            j  = nex[j];
    }
    return -1;
}

int main()
{
   char ms[10000];
   char ts[10000];
   while(scanf("%s",&ms))
   {
       if(ms[0] =='#') return 0;
       scanf("%s",&ts);
        con = 0;
       KmpMatch(ms,ts);
       printf("%d\n",con);

   }
}

hdu 4847 Wow!Such Doge!

#include<iostream>
#include<cstdio>
#include<cstring>

int doge[10];
int dogE[10];
int doGe[10];
int doGE[10];
int dOge[10];
int dOgE[10];
int dOGe[10];
int dOGE[10];
int Doge[10];
int DogE[10];
int DoGe[10];
int DoGE[10];
int DOge[10];
int DOgE[10];
int DOGe[10];
int DOGE[10];
void Buildnext(int as[],const char ts[])
{
    as[0] = -1;
    for(int i = 0, j = -1; ts[i]; )
    {
        if(j == -1 || ts[i] == ts[j])
            as[++i] = ++j;
        else j = as[j];
    }
}

void init_next()
{
    Buildnext(doge,"doge");
    Buildnext(dogE,"dogE");
    Buildnext(doGe,"doGe");
    Buildnext(doGE,"doGE");
    Buildnext(dOge,"dOge");
    Buildnext(dOGe,"dOGe");
    Buildnext(dOGE,"dOGE");
    Buildnext(Doge,"Doge");
    Buildnext(DogE,"DogE");
    Buildnext(DoGe,"DoGe");
    Buildnext(DoGE,"DoGE");
    Buildnext(DOge,"DOge");
    Buildnext(DOgE,"DOgE");
    Buildnext(DOGe,"DOGe");
    Buildnext(DOGE,"DOGE");
    Buildnext(dOgE,"dOgE");
}

int KmpMatch(char ms[], int nex[], const char ts[])
{
    int cnt = 0;
    for(int i = 0,j = 0;ms[i]; )
    {
        if(j == -1 || ms[i] == ts[j])
        {
            i++;
            j++;
        }
        else j = nex[j];
        if(j == strlen(ts))
        {
            cnt++;
            j = 0;
        }
    }
    return cnt;
}
int main()
{
    int cnt = 0;
    char s1[100000];
    init_next();
    while(scanf("%s",&s1)!= EOF)
    {
        cnt +=KmpMatch(s1,doge,"doge")+KmpMatch(s1,dogE,"dogE")+KmpMatch(s1,doGe,"doGe")+KmpMatch(s1,doGE,"doGE");
        cnt +=KmpMatch(s1,dOge,"dOge")+KmpMatch(s1,dOgE,"dOgE")+KmpMatch(s1,dOGe,"dOGe")+KmpMatch(s1,dOGE,"dOGE");
        cnt +=KmpMatch(s1,Doge,"Doge")+KmpMatch(s1,DogE,"DogE")+KmpMatch(s1,DoGe,"DoGe")+KmpMatch(s1,DoGE,"DoGE");
        cnt +=KmpMatch(s1,DOge,"DOge")+KmpMatch(s1,DOgE,"DOgE")+KmpMatch(s1,DOGe,"DOGe")+KmpMatch(s1,DOGE,"DOGE");
    }
    printf("%d\n",cnt);

}

hdu 1686、poj 3461 Oulipo

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int nex[1000000];

void Buildnext(char ts[])
{
    nex[0] = -1;
    for(int i = 0,j  = -1; ts[i]; )
    {
        if(j == -1 ||ts[i] == ts[j])
            nex[++i] = ++j;
        else j = nex[j];
    }
}

int KmpMatch(char ms[],char ts[])
{
    int cnt = 0;
    int tlen = strlen(ts);
    for(int i = 0,j = 0; ms[i]; )
    {
        if(j == -1 || ms[i] == ts[j])
        {
            i++;
            j++;
            if(j == tlen)
            {
                j = nex[j];
                cnt ++;
            }
        }
        else j = nex[j];


    }
    return cnt;
}

int main()
{
    int t;
    cin>>t;

    while(t--)
    {
        char ts[10000000], ms[10000000];
        scanf("%s %s",&ts,&ms);
        Buildnext(ts);
        printf("%d\n", KmpMatch(ms,ts));

    }
}

hdu 1711 Number Sequence

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int ts[1000000], ms[1000000];
int nex[1000000];
void Buildnext(int ts[],int len2)
{
    nex[0] = -1;
    for(int i = 0,j  = -1; i<=len2; )
    {
        if(j == -1 ||ts[i] == ts[j])
            nex[++i] = ++j;
        else j = nex[j];
    }
}

int KmpMatch(int ms[],int ts[],int tslen, int len)
{
    int cnt = 0;
    int tlen = tslen;
    for(int i = 0,j = 0; i<len; )
    {
        if(j == -1 || ms[i] == ts[j])
        {
             i++,j++;
        }
        else j = nex[j];
        if(j == tlen )
            {
                return i- tlen + 1;
            }
    }
    return -1;
}

int main()
{
    int t;
    scanf("%d",&t);

    while(t--)
    {

        int len,len2;
        scanf("%d%d",&len,&len2);
        for(int i = 0;i<len; i++) scanf("%d",&ms[i]);
        for(int i = 0;i<len2; i++) scanf("%d",&ts[i]);
        Buildnext(ts,len2);
        int ans =  KmpMatch(ms,ts,len2, len);
        printf("%d\n",ans);

    }
}

hdu 4763 Theme Section(参考)

思路:

既然要求三个相同的子串,而且有两个还必须在开头和结尾,那就求Next数组,Next数组存的是前后缀相同的长度,所以只需要找【2*i,L-Next[i]】之间相同的字串,即Next[j]==i;

#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=1000005;
 
char s[N];
int Next[N],l;
 
void get_Next()
{
    int i=0,j=-1;
    Next[0]=-1;
    while(i<l)
    {
        if(j==-1||s[i]==s[j])
            Next[++i]=++j;
        else
            j=Next[j];
    }
}
 
int KMP()
{
    int i,j;
    for(i=Next[l]; i; i=Next[i])
        for(j=2*i; j<=l-i; j++)
            if(Next[j]==i)
                return i;
    return 0;
}
 
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",s);
        int ans=0,k,i,j;
        l=strlen(s);
        get_Next();
//        for(i=1; i<=l; i++)
//            printf("%d ",Next[i]);
//        printf("\n");
        printf("%d\n",KMP());
    }
    return 0;
}

参考:https://blog.csdn.net/qq_41984014/article/details/81626493?ops_request_misc=&request_id=&biz_id=102&utm_term=theme%20section&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-81626493.nonecase&spm=1018.2226.3001.4187

poj 2046 Power Strings

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int nex[1000005];
char ms[1000005];
int cnt;
void BuildNext()
{
    nex[0] = -1;
    for(int i =0,j = -1;ms[i]; )
    {
        if(j == -1 || ms[i] == ms[j])
            nex[++i] = ++j;

        else j = nex[j];
    }
}
int main()
{
    int cyc;
    while(scanf("%s",&ms)!= EOF && strcmp(ms,".") != 0 )
    {
            BuildNext();
            int len = strlen(ms);
           
            cyc = len - nex[len];

            if(len %cyc == 0)
                printf("%d\n", len /cyc);
            else
                printf("1\n");
    }
    return 0;
}

poj 2752 Seek the Name, Seek the Fame

#include<cstdio>
#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
int nex[1000000];
char ms[1000000];
int cnt;
void BuildNext()
{
    nex[0] = -1;
    for(int i =0,j = -1;ms[i]; )
    {
        if(j == -1 || ms[i] == ms[j])
        nex[++i] = ++j;

        else j = nex[j];
    }
}

int main()
{
    while(scanf("%s",&ms) != EOF)
    {
        stack<int>q;
        int len  = strlen(ms);
        BuildNext();
        q.push(len);
        while(nex[len] != 0)
        {
            q.push(nex[len]);
            len = nex[len];
        }
        while(!q.empty())
        {
            printf("%d ",q.top());
            q.pop();
        }
        printf("\n");
    }
}

poj 2185 Milking Grid (难题)

#include<iostream>
#include<cstring>
#include<iostream>
using namespace std;
char cow[10010][80];
char rvercow[80][10010];

int nexrow[10010];
int nexcol[10010];

int row,col;

void getnexrow()
{
    int j = -1,k = 0;
    nexrow[0] = -1;
    while(k < row)
    {
        if(j == -1 || !strcmp(cow[k],cow[j]))
        {
            k++;
            j++;
            nexrow[k] = j;
        }
        else j = nexrow[j];
    }
}

void getnexcol()
{
    int j = -1,k = 0;
    nexcol[0] = -1;
    while(k < col)
    {
        if(j == -1 ||!strcmp(rvercow[k],rvercow[j]))
        {
            j++,k++;
            nexcol[k] = j;
        }
        else
            j = nexcol[j];
    }
}

int main()
{
    while(cin>>row>>col)
    {
        for(int i =0;i < row;i++)
            cin>>cow[i];
        getnexrow();

        for(int i =0; i<row; i++)
            for(int j = 0; j<col; j++)
                rvercow[j][i] = cow[i][j];
        getnexcol();

        int minn = (row - nexrow[row]) * (col - nexcol[col]);
        cout<<minn<<endl;

    }
    return 0;
}

poj 3080 Blue Jeans

#include<iostream>
#include<cstring>
#include<string>
using namespace std;

int nex[1000000];
void BuildNex(string t)
{
    nex[0] = -1;
    int i = 0,j = -1;
    int len = t.length();
    while(i <len)
    {
        if(j == -1 || t[i] == t[j])
        {
            nex[++i] = ++j;
        }
        else j = nex[j];
    }
}
int kmp(string s,string t)
{
    int i = 0, j = 0;
    int lens = s.length(),lent = t.length();
    while(i < lens && j <lent)
    {
        if(j == -1 || s[i] == t[j])
            i++,j++;
        else j = nex[j];

        if(j == lent)
            return i-j+1;
    }
    return -1;
}
int main()
{
    int nn;
    scanf("%d",&nn);
    string s[15];
    while(nn--)
    {
        int n;
        cin>>n;
        for(int i = 0; i<n;i++) cin>>s[i];
        string t;
        string res;
        int ma = 0;
        for(int i = 0;i < 60;i++)
        {
            for(int j = i+1; j<= 60;j++)
            {
                t = string(s[0].begin()+i,s[0].begin()+j);
                BuildNex(t);
                int f = 0;
                for(int k = 1;k<n; k++)
                {
                    if(kmp(s[k],t) == -1)
                    {
                        f = 1;
                        break;
                    }
                }

                if(!f)
                {
                    if(ma<= j-i)
                    {
                        if(ma<j-i) res = t;
                        else res = min(res,t);
                        ma = j - i;
                    }
                }
            }
        }
        if(ma <3) printf("no significant commonalities\n");
        else
            cout<<res<<endl;
    }

}

hdu 1358 Period

#include<cstdio>
#include<iostream>
using namespace std;

int nex[1000005];
char ts[1000005];
void Buildnex(char ts[])
{
    nex[0] = -1;
    for(int i = 0, j = -1;ts[i]; )
    {
        if(j == -1 || ts[i] == ts[j])
            nex[++i] = ++j;
        else j = nex[j];
    }


}
int times;
int main()
{
    int t;
    times = 0;
    while(scanf("%d",&t)!=EOF)
    {
        if(t == 0)
            return 0;
        times++;

            scanf("%s",ts);

        Buildnex(ts);
        printf("Test case #%d\n",times);
        for(int i = 2;i<=t;i++)
        {
            if(i%(i-nex[i]) == 0 && i/(i-nex[i]) > 1)
            {
                cout<<i<<" "<<i/(i-nex[i])<<endl;
            }
        }
        cout<<endl;
    }

}

hdu 3746 Cyclic Nacklace

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int nex[1000005];
char ts[1000005];

void Buildnex(char ts[])
{
    nex[0] = -1;
    for(int i =0,j = -1;ts[i]; )
    {
        if(j == -1 || ts[i] == ts[j])
        {
            nex[++i] = ++j;
        }
        else j = nex[j];
    }
}

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%s",&ts);
        Buildnex(ts);
        int len = strlen(ts);
        if(len % (len -nex[len]) == 0 && len != (len -nex[len]))
            cout<<0<<endl;
        else
        cout<<(len -nex[len]) -len %(len-nex[len])<<endl;

    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值