我们仍未知道的那天所看见的KMP的精髓

题目描述

如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置。

为了减少骗分的情况,接下来还要输出子串的前缀数组next。

(如果你不知道这是什么意思也不要问,去百度搜[kmp算法]学习一下就知道了。

输入格式

第一行为一个字符串,即为s1

第二行为一个字符串,即为s2

输出格式

若干行,每行包含一个整数,表示s2在s1中出现的位置

接下来1行,包括length(s2)个整数,表示前缀数组next[i]的值。


 失配数组nt[]

 

#include<bits/stdc++.h>
#define re return
#define inc(i,l,r) for(int i=l;i<=r;++i)
using namespace std;
template<typename T>inline void rd(T&x)
{
    char c;bool f=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
    x=c^48;
    while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
    if(f)x=-x; 
}

int n,m,p[1000006];
//p[i]是指p-1与p[i]-1恰好配对到一起 
char s[1000006],ss[1000006];

inline void pre_kmp()
{
    int j=0;
    p[0]=-1;//防止越界 
    inc(i,0,m-1)
    {
        j=p[i];
        while(j!=-1&&ss[j]!=ss[i])j=p[j];//向前寻找失配数组 
        p[i+1]=(~j)?(ss[i]==ss[j]?++j:0):0;//i,j互配后i+1的失配值 
    }
}


inline void ing_KMP()
{
    int j=0;
    inc(i,0,n-1)
    {
        while(j!=-1&&s[i]!=ss[j])j=p[j];
        //防止越界,寻找配对 
        ++j;
        if(j==m)//找到答案 
        {
            printf("%d\n",i+1-j+1);
            j=p[j];
            //继续失配 
        }
    }
}
int main()
{
    freopen("in.txt","r",stdin);
    scanf("%s%s",s,ss);
    n=strlen(s);m=strlen(ss);
    pre_kmp();//计算子串的nt数组 
    ing_KMP();//配对 
    
    inc(i,1,m)
    printf("%d ",p[i]);
    //输出失配位置,为什么会是p[i],因为ss从0开始p[i-1]恰好为所求值 
    re 0;
}

 

 

 如果一直向前面那样理解,反正我是永远不会KMP

但是我觉得下面一种非常好背

 

#include<bits/stdc++.h>
#define re return
#define inc(i,l,r) for(int i=l;i<=r;++i)

const int maxn=1000005;

using namespace std;
int n,m,nt[maxn];
char s[maxn],ss[maxn];

//nt表示与当前置可互配的最长前缀 

inline void KMP()
{
    nt[1]=0;
    //理所当然nt[1]=0 
    inc(i,2,m)
    {
        int j=nt[i-1];//与上一位的可配的下一位配对 
        while(j&&ss[j+1]!=ss[i])j=nt[j];
        if(ss[j+1]==ss[i])++j;
        nt[i]=j;
    }
}

inline void Get_ans()
{
    int j=0;
    inc(i,1,n)
    {
        while(j&&ss[j+1]!=s[i])j=nt[j];
        ++j;
        if(j==m)
        {
            printf("%d\n",i-m+1); 
        }
    }
}

int main()
{
    freopen("in.txt","r",stdin);
    scanf("%s%s",s+1,ss+1);
    n=strlen(s+1);
    m=strlen(ss+1);
    
    KMP();
    Get_ans();
    
    inc(i,1,m)
    printf("%d ",nt[i]);
    re 0;
} 

 

 

posted @ 2019-08-18 20:23  凉如水  阅读(125)  评论(0)    收藏  举报