KMP & 扩展KMP & Manacher 专题
先来模版:
void getNext(int *b,int m) { Next[0]=-1; int i=0,j=-1; while(i<m&&j<m){ if(j==-1||b[i]==b[j]) Next[++i]=++j; else j=Next[j]; } } int kmp(int *a,int *b,int n,int m) { getNext(b,m); int i=0,j=0; while(i<n&&j<m){ if(j==-1||a[i]==b[j]) i++,j++; else j=Next[j]; } if(j==m) return i-m; return -1; }
模版很容易理解,现在都可以手写了。
这段时间就做kmp了,下面是按做题顺序的,随时更新....
C题:
求串s中串t出现的个数,重叠不算。每次找到后继续kmp即可,由于串t不变,所以不用多次求next数组。话说把kmp的参数加上n和m会更方便灵活的处理一些题目。
http://www.cnblogs.com/--560/p/4555324.html
A题:
简单的kmp水题。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/A
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #include<string> #include<math.h> #include<cctype> #define ll long long #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t)) #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--) #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t)) #define PII pair<int,int> #define fst first #define snd second #define MP make_pair #define PB push_back #define RI(x) scanf("%d",&(x)) #define RII(x,y) scanf("%d%d",&(x),&(y)) #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z)) #define DRI(x) int (x);scanf("%d",&(x)) #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y)) #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d",&(x),&(y),&(z)) #define RS(x) scanf("%s",s) #define RSS(x,y) scanf("%s%s",x,y) #define DRS(x) char x[maxn];scanf("%s",x) #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y) #define MS0(a) memset((a),0,sizeof((a))) #define MS1(a) memset((a),-1,sizeof((a))) #define MS(a,b) memset((a),(b),sizeof((a))) #define ALL(v) v.begin(),v.end() #define SZ(v) (v).size() using namespace std; const int maxn=1000100; const int INF=(1<<29); const double EPS=0.0000000001; const double Pi=acos(-1.0); int a[maxn],b[maxn]; int n,m; int Next[maxn]; void getNext(int *b,int m) { Next[0]=-1; int i=0,j=-1; while(i<m&&j<m){ if(j==-1||b[i]==b[j]) Next[++i]=++j; else j=Next[j]; } } int kmp(int *a,int *b,int n,int m) { getNext(b,m); int i=0,j=0; while(i<n&&j<m){ if(j==-1||a[i]==b[j]) i++,j++; else j=Next[j]; } if(j==m) return i-m+1; return -1; } int main() { DRI(T); while(T--){ RII(n,m); REP(i,0,n-1) RI(a[i]); REP(i,0,m-1) RI(b[i]); cout<<kmp(a,b,n,m)<<endl; } return 0; }
B题:
求串s中t出现的次数,可重叠。直接在模版上改就可以了,当匹配结束的时候,将t移到合适的位置并且次数+1,继续匹配,即加上j=next[j],res++即可。
下面的代码感觉可以做模版了。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #include<string> #include<math.h> #include<cctype> #define ll long long #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t)) #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--) #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t)) #define PII pair<int,int> #define fst first #define snd second #define MP make_pair #define PB push_back #define RI(x) scanf("%d",&(x)) #define RII(x,y) scanf("%d%d",&(x),&(y)) #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z)) #define DRI(x) int (x);scanf("%d",&(x)) #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y)) #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d",&(x),&(y),&(z)) #define RS(x) scanf("%s",s) #define RSS(x,y) scanf("%s%s",x,y) #define DRS(x) char x[maxn];scanf("%s",x) #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y) #define MS0(a) memset((a),0,sizeof((a))) #define MS1(a) memset((a),-1,sizeof((a))) #define MS(a,b) memset((a),(b),sizeof((a))) #define ALL(v) v.begin(),v.end() #define SZ(v) (v).size() using namespace std; const int maxn=1000100; const int INF=(1<<29); const double EPS=0.0000000001; const double Pi=acos(-1.0); char s[maxn],t[maxn]; int n,m; int Next[maxn]; void getNext(char *t) { Next[0]=-1; int i=0,j=-1; int m=strlen(t); while(i<m&&j<m){ if(j==-1||t[i]==t[j]) Next[++i]=++j; else j=Next[j]; } } int kmp_cnt(char *s,char *t) { getNext(t); int i=0,j=0; int n=strlen(s),m=strlen(t); int res=0; while(i<n){ if(j==-1||s[i]==t[j]) i++,j++; else j=Next[j]; if(j==m) j=Next[j],res++; } return res; } int main() { DRI(T); while(T--){ RSS(t,s); int ans=0; getNext(t); cout<<kmp_cnt(s,t)<<endl; } return 0; }
E题:
遍历s的循环前缀串中的循环子串以及循环次数。
这题就是Next数组的应用了。当Next[5]=3时,有s[0]=s[2]=s[4],s[1]=s[3]=s[5],所以Next[k]=u时,前缀长度为k+1,可能循环节为(k-u),当(k+1)%(k-u)时,存在循环节(k-u)。
注意,判断next[k]=u=0的情况,因为a[k]不管等不等于a[0],next[k]都可以等于0,这时加上a[k]是否等于a[u]的判断就可以了。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/E
下面的代码感觉也可以做模版了。。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #include<string> #include<math.h> #include<cctype> #define ll long long #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t)) #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--) #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t)) #define PII pair<int,int> #define fst first #define snd second #define MP make_pair #define PB push_back #define RI(x) scanf("%d",&(x)) #define RII(x,y) scanf("%d%d",&(x),&(y)) #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z)) #define DRI(x) int (x);scanf("%d",&(x)) #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y)) #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d",&(x),&(y),&(z)) #define RS(x) scanf("%s",s) #define RSS(x,y) scanf("%s%s",x,y) #define DRS(x) char x[maxn];scanf("%s",x) #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y) #define MS0(a) memset((a),0,sizeof((a))) #define MS1(a) memset((a),-1,sizeof((a))) #define MS(a,b) memset((a),(b),sizeof((a))) #define ALL(v) v.begin(),v.end() #define SZ(v) (v).size() using namespace std; const int maxn=1000100; const int INF=(1<<29); const double EPS=0.0000000001; const double Pi=acos(-1.0); int n; char s[maxn]; int Next[maxn]; void getNext(char *s,int n) { Next[0]=-1; int i=0,j=-1; while(i<n&&j<n){ if(j==-1||s[i]==s[j]) Next[++i]=++j; else j=Next[j]; } } int main() { int tag=1; while(cin>>n,n){ RS(s); getNext(s,n); printf("Test case #%d ",tag++); REP(i,1,n-1){ int t=Next[i]; if((i+1)%(i-t)==0&&s[t]==s[i]){ printf("%d %d ",i+1,(i+1)/(i-t)); } } puts(""); } return 0; }
西电oj1012,重复序列:
这题和上一题一样,不过是求后缀,逆转即可。
http://acm.xidian.edu.cn/problem.php?id=1012
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #include<string> #include<math.h> #include<cctype> #define ll long long #define REP(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) #define REPP(i,a,b,t) for(int (i)=(a);(i)<=(b);(i)+=(t)) #define rep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--) #define repp(i,a,b,t) for(int (i)=(a);(i)>=(b);(i)-=(t)) #define PII pair<int,int> #define fst first #define snd second #define MP make_pair #define PB push_back #define RI(x) scanf("%d",&(x)) #define RII(x,y) scanf("%d%d",&(x),&(y)) #define RIII(x,y,z) scanf("%d%d%d",&(x),&(y),&(z)) #define DRI(x) int (x);scanf("%d",&(x)) #define DRII(x,y) int (x),(y);scanf("%d%d",&(x),&(y)) #define DRIII(x,y,z) int (x),(y),(z);scanf("%d%d",&(x),&(y),&(z)) #define RS(x) scanf("%s",x) #define RSS(x,y) scanf("%s%s",x,y) #define DRS(x) char x[maxn];scanf("%s",x) #define DRSS(x,y) char x[maxn],y[maxn];scanf("%s%s",x,y) #define MS0(a) memset((a),0,sizeof((a))) #define MS1(a) memset((a),-1,sizeof((a))) #define MS(a,b) memset((a),(b),sizeof((a))) #define ALL(v) v.begin(),v.end() #define SZ(v) (v).size() using namespace std; const int maxn=1000100; const int INF=(1<<29); const double EPS=0.0000000001; const double Pi=acos(-1.0); char s[maxn],t[maxn]; int n; int Next[maxn]; char ans[maxn]; int cnt; void getNext(char *s,int n) { Next[0]=-1; int i=0,j=-1; while(i<n&&j<n){ if(j==-1||s[i]==s[j]) Next[++i]=++j; else j=Next[j]; } } int main() { while(~RS(t)){ int n=strlen(t); REP(i,0,n-1) s[i]=t[n-i-1]; s[n]='