KMP & 扩展KMP & Manacher 专题
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\n",tag++); REP(i,1,n-1){ int t=Next[i]; if((i+1)%(i-t)==0&&s[t]==s[i]){ printf("%d %d\n",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]='\0'; getNext(s,n); cnt=0; MS0(ans); REP(i,1,n-1){ int j=Next[i]; if((i+1)%(i-j)==0&&s[i]==s[j]){ int tmp=(i+1)/(i-j); if(tmp>cnt){ cnt=tmp; strcpy(ans,t+(n-(i-j))); } else if(tmp==cnt){ if(i-j>strlen(ans)){ cnt=tmp; strcpy(ans,t+(n-(i-j))); } } } } if(cnt) printf("%s %d\n",ans,cnt); else puts("-1"); } return 0; }
F题:
对字符串A,从AAAAA中截取一段B,已知B,求A的最短长度。
比如 A="abcd" AAA="abcd abcd abcd" ,则截取B="bcdabcdabc"。
思路:其实这题很水,直接看最后一个字符n-1和next[n-1]就行了,如果next[n-1]=k,则n-k即为最短长度,加上s[n-1]==s[k]?的特判就可以了。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/F
#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]; int n; 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() { while(~RS(s)){ n=strlen(s); getNext(s,n); if(s[n-1]==s[Next[n-1]]) cout<<(n-1)-Next[n-1]<<endl; else cout<<n<<endl; } return 0; }
G题:
给出S,找出循环节A,S=A^n,输出循环次数n,不重叠。
这题也很水,直接找最后一个字符,看 (n-1)-Next[n-1]能不能整除n即可,同样加上s[n-1]==s[Next[n-1]]?的特判。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/G
#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]; int n; 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() { while(~RS(s)){ if(strcmp(s,".")==0) break; int n=strlen(s); getNext(s,n); int t=Next[n-1]; if(s[n-1]==s[t]&&n%(n-1-t)==0) printf("%d\n",n/(n-1-t)); else puts("1"); } return 0; }
D题:
题目真是太长了,题意才弄懂,就是求添加最少的字符使s成为循环串,所谓循环串就是s=A^n,n>1的串。
同样找最后一个字符即可。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/D
#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]; int n; 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() { DRI(T); while(T--){ RS(s); n=strlen(s); getNext(s,n); int t=Next[n-1]; if(s[n-1]==s[t]&&n%(n-1-t)==0) cout<<0<<endl; else if(s[n-1]==s[t]) cout<<n-1-t-n%(n-1-t)<<endl; else cout<<n<<endl; } return 0; }
H题:
找出一个字符串s的既是前缀又是后缀的子串。
看看Next数组中n-1和Next[n-1]的关系就明白了,从0到Next[n-1]的前缀肯定和n-Next[n-1]到n-1的后缀是匹配的,然后0到Next[Next[n-1]]同理。下面代码由于Next[n]=Next[n-1]+1,所以用n更方便。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/H
#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]; int n; 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() { while(~RS(s)){ n=strlen(s); getNext(s,n); stack<int> ans; for(int p=n;p!=0;p=Next[p]){ ans.push(p); } while(!ans.empty()){ cout<<ans.top()<<" "; ans.pop(); } puts(""); } return 0; }
I题:
给一组长度相等的字符串,找出最长公共子串。(len<60,m<=10)
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/I
这道题是poj上的,数据非常小,之前直接暴力+STL的找子串函数find水过。。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<queue> #include<stack> #include<vector> #include<map> #include<set> #include<cmath> #include<string> using namespace std; const int maxn=1000010; const int INF=(1<<28); int T; int n; string str[maxn]; void solve() { for(int len=60;len>=3;len--){ int flag=0; string ans=""; for(int st=0;st+len<=60;st++){ string tmp=str[0].substr(st,len); int tag=1; for(int i=1;i<n;i++){ if(str[i].find(tmp)==string::npos){ tag=0; break; } } if(tag){ flag=1; if(ans=="") ans=tmp; else if(ans>tmp) ans=tmp; } } if(flag){ cout<<ans<<endl; return; } } cout<<"no significant commonalities"<<endl; } int main() { cin>>T; while(T--){ cin>>n; for(int i=0;i<n;i++) cin>>str[i]; solve(); } return 0; }
这次既然学了kmp就应该用kmp了吧,在这里kmp只是个匹配子串的工具,还是暴力,可能暴力的姿势不好,效率比STL还差了点。
暴力的思路是,从大到小枚举长度,再枚举相同长度的位置。
#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=1200; const int INF=(1<<29); const double EPS=0.0000000001; const double Pi=acos(-1.0); string s[maxn]; int M; int Next[maxn]; void getNext(string s) { Next[0]=-1; int i=0,j=-1; int m=s.length(); while(i<m&&j<m){ if(j==-1||s[i]==s[j]) Next[++i]=++j; else j=Next[j]; } } int kmp(string s,string t) { int n=s.length(),m=t.length(); int i=0,j=0; while(i<n&&j<m){ if(j==-1||s[i]==t[j]) i++,j++; else j=Next[j]; } if(j==m) return i-m; return -1; } int main() { DRI(T); while(T--){ RI(M); REP(i,0,M-1) cin>>s[i]; string ans="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"; int n=s[0].length(); bool flag=0; rep(i,n,3){ REP(j,0,n-i){ string t=s[0].substr(j,i); getNext(t); bool tag=1; REP(k,1,M-1){ if(kmp(s[k],t)==-1) tag=0; if(!tag) break; } if(tag){ if(t<ans) ans=t; flag=1; } } if(flag) break; } if(flag) cout<<ans<<endl; else puts("no significant commonalities"); } return 0; }
J题:
求既是s的前缀又是t的后缀的最长子串。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/J
很简单,也很好理解,还是直接在kmp的模版上改动即可,匹配时,让s和t匹配,匹配到t在s出现时,如果i!=n继续匹配,直到i==n时前后缀匹配成功,因此只需将原模版中的j<m去掉即可。
以为是t的前缀s的后缀也可以,于是就写了两次。。。
#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); string s,t; int n,m; int nexts[maxn],nextt[maxn]; void getNext(string a,int n,int *next) { next[0]=-1; int i=0,j=-1; while(i<n&&j<n){ if(j==-1||a[i]==a[j]) next[++i]=++j; else j=next[j]; } } string kmp(string s,string t,int *next) { int n=s.length(),m=t.length();; int i=0,j=0; while(i<n){ if(j==-1||s[i]==t[j]) i++,j++; else j=next[j]; } return t.substr(0,j); } int main() { while(cin>>s>>t){ n=s.length(),m=t.length();; getNext(s,n,nexts); string ans=kmp(t,s,nexts); if((int)ans.length()!=0) cout<<ans<<" "<<(int)ans.length()<<endl; else puts("0"); } return 0; }
西电oj1037,倍流畅序列
之前由于数据太小,直接暴力过,要是改成10^6,就得用kmp了,就是简单的匹配前后缀而已。
http://acm.xidian.edu.cn/problem.php?id=1037
#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]; void getNext(char *t,int n) { Next[0]=-1; int i=0,j=-1; while(i<n&&j<n){ if(j==-1||t[i]==t[j]) Next[++i]=++j; else j=Next[j]; } } int main() { DRI(T); while(T--){ RS(s); n=strlen(s); REP(i,0,n-1) t[i]=s[n-1-i]; REP(i,0,n-1) t[i]=(1-(t[i]-'0'))+'0'; t[n]='\0'; getNext(t,n); int i=0,j=0; while(i<n){ if(j==-1||s[i]==t[j]) i++,j++; else j=Next[j]; } printf("%s%s\n",s,t+j); } return 0; }
K题:
求s的所有前缀子串在s中出现的次数之和,可重叠。
很容易想到dp的思路,也就是用cnt数组记录。
kmp+dp。 dp[i]=dp[next[j]]+1,dp[i]表示以i为结尾的前缀在0-i中出现的次数。
这题做得真是郁闷,i从0到n-1没过,改为1到n就过了,杭电的数据和西工大有得一拼,坑坑坑坑坑。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/K
#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); const int MOD=10007; int n; char s[maxn]; int Next[maxn],cnt[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() { DRI(T); while(T--){ RI(n);RS(s); getNext(s,n); MS0(cnt); REP(i,1,n){ int j=Next[i]; cnt[i]=(cnt[j]+1)%MOD; } ll ans=0; REP(i,1,n) ans=(ans%MOD+cnt[i]%MOD)%MOD; cout<<ans<<endl; } return 0; }
J题:
我承认cf的题我随便看一眼就懂了,看杭电的盯着屏幕看了半天配合各种词典谷歌翻译都不知道在扯什么。。
这题意思是这样的,给出一个由"密文+明文"组成的字符串和明文密文转换表,其中明文部分可能有所丢失,让你恢复该字符串。
将输入的"密文+不完全明文”的字符串按表转换为“未知+不完全密文",和原字符串匹配前后缀。
很简单的匹配前后缀,就是题意描述不忍吐槽。。。
注意下匹配时匹配成功的部分长度必须长于其余部分就可以了,否则继续匹配。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/L
#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); string table; char table2[maxn]; string s,t; int n; int Next[maxn]; void getNext(string 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() { DRI(T); while(T--){ cin>>table>>s; REP(i,0,25) table2[table[i]-'a']='a'+i; n=s.length(); t=""; REP(i,0,n-1) t+=table[s[i]-'a']; getNext(s,n); int i=0,j=-1; while(i<n){ if(j==-1||t[i]==s[j]) i++,j++; else j=Next[j]; if(i==n&&j>n-j) j=Next[j-1],i--; } string ans=s.substr(0,n-j); cout<<ans; REP(i,0,(int)ans.length()-1) printf("%c",table2[ans[i]-'a']); cout<<endl; } return 0; }
M题:
这题就不多说什么了,就是暴力枚举,和I题一样,数据小,要是平时就直接用STL水了,不过在这专题还是用一下kmp。
另外也学了STL的字符串反转reverse。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/M
#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) (int)(v).size() using namespace std; const int maxn=1100; const int INF=(1<<29); const double EPS=0.0000000001; const double Pi=acos(-1.0); int n; string s[maxn]; int Next[maxn]; void getNext(string 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 kmp(string s,string t) { int n=SZ(s),m=SZ(t); int i=0,j=0; while(i<n&&j<m){ if(j==-1||s[i]==t[j]) i++,j++; else j=Next[j]; } if(j==m) return i-m; return -1; } int main() { DRI(T); while(T--){ RI(n); REP(i,0,n-1) cin>>s[i]; bool flag=0; int ans=0; rep(i,SZ(s[0]),1){ REP(j,0,SZ(s[0])-i){ string s1=s[0].substr(j,i); getNext(s1,SZ(s1)); flag=1; REP(i,1,n-1){ if(kmp(s[i],s1)==-1){ flag=0;break; } } if(flag){ ans=i;break; } reverse(ALL(s1)); flag=1; REP(i,1,n-1){ if(kmp(s[i],s1)==-1){ flag=0;break; } } if(flag){ ans=i;break; } } if(flag) break; } cout<<ans<<endl; } return 0; }
N题:
这题和还是暴力枚举子串+kmp匹配,由于数据有点大,就不用STL水了。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/N
#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) (int)(v).size() using namespace std; const int maxn=1100; const int INF=(1<<29); const double EPS=0.0000000001; const double Pi=acos(-1.0); string s[maxn]; int n; int Next[maxn]; void getNext(string a,int n) { Next[0]=-1; int i=0,j=-1; while(i<n&&j<n){ if(j==-1||a[i]==a[j]) Next[++i]=++j; else j=Next[j]; } } int kmp(string s,string t) { int n=s.length(),m=t.length(); int i=0,j=0; while(i<n&&j<m){ if(j==-1||s[i]==t[j]) i++,j++; else j=Next[j]; } if(j==m) return i-m; return -1; } int main() { while(cin>>n,n){ REP(i,0,n-1) cin>>s[i]; string ans=""; bool flag=0; rep(i,SZ(s[0]),1){ REP(j,0,SZ(s[0])-i){ string t=s[0].substr(j,i); getNext(t,SZ(t)); bool tag=1; REP(i,1,n-1){ if(kmp(s[i],t)==-1){ tag=0;break; } } if(tag){ flag=1; if(t<ans||ans=="") ans=t; } } if(flag) break; } if(flag) cout<<ans<<endl; else cout<<"IDENTITY LOST"<<endl; } return 0; }
O题:
循环字符串的最大最小表示法+kmpNext数组找循环节。
本来想直接匹配的,但是WA了。。。没找到WA的数据。。
#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) (int)(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]; int n; int Next[maxn]; char xs[maxn],ys[maxn],ts[maxn]; char txs[maxn],tys[maxn]; int getMin(char*s) { int n=strlen(s); int i=0,j=1,k=0; while(i<n&&j<n&&k<n){ int t=s[(i+k)%n]-s[(j+k)%n]; if(!t) k++; else{ if(t>0) i+=k+1; else j+=k+1; if(i==j) j++; k=0; } } return min(i,j); } int getMax(char*s) { int n=strlen(s); int i=0,j=1,k=0; while(i<n&&j<n&&k<n){ int t=s[(i+k)%n]-s[(j+k)%n]; if(!t) k++; else{ if(t<0) i+=k+1; else j+=k+1; if(i==j) j++; k=0; } } return min(i,j); } void getNext(char*t) { int m=strlen(t); Next[0]=-1; int i=0,j=-1; while(i<n&&j<n){ if(j==-1||t[i]==t[j]) Next[++i]=++j; else j=Next[j]; } } int main() { while(~RS(s)){ n=strlen(s); int x=getMin(s),y=getMax(s); getNext(s); int cnt=1; if(n%(n-Next[n])==0) cnt=n/(n-Next[n]); cout<<x+1<<" "<<cnt<<" "<<y+1<<" "<<cnt<<endl; } return 0; }
P题:
循环字符串的最小表示+map统计次数,水题
#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%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) (int)(v).size() using namespace std; const int maxn=10100; const int INF=(1<<29); const double EPS=0.0000000001; const double Pi=acos(-1.0); int n; string s[maxn]; map<string,int> cnt; int getMin(string s) { int n=SZ(s); int i=0,j=1,k=0; while(i<n&&j<n&&k<n){ int t=s[(i+k)%n]-s[(j+k)%n]; if(!t) k++; else{ if(t>0) i+=k+1; else j+=k+1; if(i==j) j++; k=0; } } return min(i,j); } int main() { //freopen("in.txt","r",stdin); while(~RI(n)){ cnt.clear(); int ans=0; REP(i,1,n){ cin>>s[i]; int x=getMin(s[i]),L=SZ(s[i]); s[i]+=s[i]; s[i]=s[i].substr(x,L); if(!cnt[s[i]]) cnt[s[i]]++,ans++; } cout<<ans<<endl; } return 0; }
Q题:
找出字符串s的所有周期前缀,周期前缀就是s.substr(0,p-1),s[i]=s[i+p](0<=i<len)。
其实就是前缀匹配后缀,和H题是同一道题,这里的前缀应该是s.substr(0,len-p),直接利用next数组的性质就行了,参照H题。
#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%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) (int)(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); string s; int Next[maxn]; void getNext(string s) { Next[0]=-1; int i=0,j=-1; int n=SZ(s); while(i<n&&j<n){ if(j==-1||s[i]==s[j]) Next[++i]=++j; else j=Next[j]; } } int main() { //freopen("in.txt","r",stdin); DRI(T); REP(casen,1,T){ cin>>s; int len=SZ(s); getNext(s); vector<int> ans; for(int p=len;Next[p]!=-1;p=Next[p]){ ans.PB(len-Next[p]); } printf("Case #%d: %d\n",casen,SZ(ans)); REP(i,0,SZ(ans)-2) cout<<ans[i]<<" "; cout<<ans[SZ(ans)-1]<<endl; } return 0; }
S题:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/S
先给定26个字母的权值,可为负。
将字符串s切割成左右两部分(两部分的长度均需大于0),计算求左右两部分的权值和的最大值。如果一个字符串t为回文,那么权值和为字母权值之和,否则为0。
枚举所有的切割位置,更新ans的值。
关键在于如何避开判断回文的复杂度。所谓回文就是逆转之后还能和原串相等的串,因此可以通过kmp匹配原串和逆转串的前后缀,从而判断回文并记录。
#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%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) (int)(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); string s,t; int val[30]; int Next[maxn]; ll sum[maxn]; bool ispalin[2][maxn]; void getNext(string s) { int n=SZ(s); 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() { //freopen("in.txt","r",stdin); DRI(T); while(T--){ REP(i,0,25) RI(val[i]); cin>>s; int len=SZ(s); MS0(sum); sum[0]=val[s[0]-'a']; REP(i,1,len-1) sum[i]=sum[i-1]+val[s[i]-'a']; t=""; REP(i,0,len-1) t+=s[len-1-i]; MS0(ispalin); getNext(s); int i=0,j=-1; while(i<len&&j<len){ if(j==-1||t[i]==s[j]) i++,j++; else j=Next[j]; } if(j<len){ for(int p=j;p!=-1;p=Next[p]) ispalin[0][p]=1; } getNext(t); i=0,j=-1; while(i<len&&j<len){ if(j==-1||s[i]==t[j]) i++,j++; else j=Next[j]; } if(j<len){ for(int p=j;p!=-1;p=Next[p]) ispalin[1][len-p]=1; } ll ans=-INF; REP(i,1,len-1){ ll tmp=0; if(ispalin[0][i]) tmp+=sum[i-1]; if(ispalin[1][i]) tmp+=sum[len-1]-sum[i-1]; if(tmp>ans) ans=tmp; } cout<<ans<<endl; } return 0; }
U题:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/U
求最长回文子串,Manacher算法。复杂度o(n)。
#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%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) (int)(v).size() using namespace std; const int maxn=5000100; const int INF=(1<<29); const double EPS=0.0000000001; const double Pi=acos(-1.0); char s[maxn],t[maxn]; int p[maxn]; int Manacher(char *s) { int len=strlen(s); REP(i,0,len) t[2*i]='#',t[2*i+1]=s[i]; len=strlen(t); int id=0,R=0; MS0(p); REP(i,0,len-1){ if(id+p[id]<i){ int r=0; while(i-r>=0&&i+r<len&&t[i-r]==t[i+r]) r++; p[i]=r-1; } else{ int k=i-id; if(id-p[id]>id-k-p[id-k]) p[i]=id-k-(id-p[id]); else if(id-p[id]<id-k-p[id-k]) p[i]=p[id-k]; else{ int r=p[id-k]; while(i-r>=0&&i+r<len&&t[i-r]==t[i+r]) r++; p[i]=r-1; } } if(p[i]>R) R=p[i]; if(i+p[i]>id+p[id]) id=i; } return R; } int main() { freopen("in.txt","r",stdin); int casen=1; while(RS(s)&&strcmp(s,"END")){ printf("Case %d: %d\n",casen++,Manacher(s)); } return 0; }
另有后缀数组解法-------
X题:
求最长回文子串,Manacher算法。
#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%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) (int)(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 p[maxn]; int Manacher(char *s) { int len=strlen(s); REP(i,0,len) t[2*i]='#',t[2*i+1]=s[i]; int R=0,id=0; len=strlen(t); MS0(p); REP(i,0,len-1){ if(i>id+p[id]){ int r=0; while(i-r>=0&&i+r<len&&t[i-r]==t[i+r]) r++; p[i]=r-1; } else{ int k=i-id; if(id-k-p[id-k]<id-p[id]) p[i]=p[id]-k; else if(id-k-p[id-k]>id-p[id]) p[i]=p[id-k]; else{ int r=p[id-k]; while(i-r>=0&&i+r<len&&t[i-r]==t[i+r]) r++; p[i]=r-1; } } if(p[i]>R) R=p[i]; if(i+p[i]>id+p[id]) id=i; } return R; } int main() { //freopen("in.txt","r",stdin); while(~RS(s)){ cout<<Manacher(s)<<endl; } return 0; }
V题:
求满足先递增后递减的最长回文子串。Manacher算法。
#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%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) (int)(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,a[maxn],b[maxn]; int p[maxn]; int Manacher() { MS0(p); REP(i,0,n) b[i*2]=-1,b[i*2+1]=a[i]; n=n*2+1; int R=0,id=0; REP(i,0,n-1){ if(i>id+p[id]){ int r=1; while(i-r>=0&&i+r<n&&b[i-r]==b[i+r]&&b[i-r]<=b[i-r+2]) r++; p[i]=r-1; } else{ int k=i-id; if(id-k-p[id-k]<id-p[id]) p[i]=p[id]-k; else if(id-k-p[id-k]>id-p[id]) p[i]=p[id-k]; else{ int r=p[id-k]+1; while(i-r>=0&&i+r<n&&b[i-r]==b[i+r]&&b[i-r]<=b[i-r+2]) r++; p[i]=r-1; } } if(p[i]>R) R=p[i]; if(i+p[i]>id+p[id]) id=i; } return R; } int main() { freopen("in.txt","r",stdin); DRI(T); while(T--){ RI(n); REP(i,0,n-1) RI(a[i]); cout<<Manacher()<<endl; } return 0; }
W题:
求最长回文子串。Manacher算法。
Z题:
对字符串s,找最长的既是前缀也是后缀而且在中间出现过且不重叠。这题之前在省选pk赛出现过,当时没做出来,现在学了kmp当然可以直接秒了。
#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%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) (int)(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); string s; int Next[maxn],Next1[maxn]; void getNext(string s,int *Next) { Next[0]=-1; int n=SZ(s); int i=0,j=-1; while(i<n&&j<n){ if(j==-1||s[i]==s[j]) Next[++i]=++j; else j=Next[j]; } } bool kmp(string s,string t,int &L) { int n=SZ(s),m=SZ(t); int i=0,j=-1; while(i<n&&j<m){ if(j==-1||s[i]==t[j]) i++,j++; else j=Next[j]; } if(j==m){ L=m;return 1; } return 0; } int main() { freopen("in.txt","r",stdin); DRI(T); while(T--){ cin>>s; getNext(s,Next); int n=SZ(s); int ans=0; for(int p=Next[n];p!=-1&&p;p=Next[p]){ if(p>n/3) continue; string t=s.substr(0,p); getNext(t,Next1); int x=p-1,y=n-p; if(y-x+1<SZ(t)) continue; int L=0; if(kmp(s.substr(x,y-x+1),t,L)){ if(L>ans) ans=L; } } cout<<ans<<endl; } return 0; }
======================================================================================================================
kmp和manacher刷得差不多了,下一专题:数位dp