我们仍未知道的那天所看见的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; }

浙公网安备 33010602011771号