题意:给你两个串,问你第一个串在第二个串出现的次数
解题思路:模板题
代码
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; const int maxn=1e6+100; const int maxm=1e4+100; char s[maxn]; char t[maxm]; int _next[maxm]; int slen,tlen; void get_next() { memset(_next,0,sizeof(_next)); int j,k;//k代表的是相同字符的长度; j=0;k=-1;_next[0]=-1; while(j<tlen)//计算模式串的next数组,也就是每个字符若是匹配失败,该跳的位置; { if(k==-1||t[j]==t[k])//如果相等等于之前的+1; _next[++j]=++k; else k=_next[k];//如果不相等,跳转到k=next[k]去;把指针转移到之前的相同串去,保证最大前缀 } } int kmp_count() { int ans=0,i=0,j=0; if(slen==1&&tlen==1) { if(s[0]==t[0]) return 1; else return 0; } get_next(); while(i<slen) { if(j==-1||s[i]==t[j]) { i++;j++; } else j=_next[j]; if(j==tlen) { ans++; j=_next[j]; } } return ans; } int main() { int tt; scanf("%d",&tt); while(tt--) { scanf("%s%s",t,s); tlen=strlen(t);slen=strlen(s); cout<<kmp_count()<<endl; } }
B - Number Sequence
题意:给你两个数字字符串,问你第二个串在第一个串首次出现的位置
解题思路:模板题
代码
#include<algorithm> #include<cstdio> #include<cstring> #include<queue> #include<vector> #include<map> #include<set> #include<iostream> using namespace std; const int maxn=1e6+100; const int maxm=1e4+100; int t[maxm],s[maxn]; int tlen,slen,_next[maxm]; void get_next() { int j=0,k=-1;_next[0]=-1; while(j<tlen) { if(k==-1||t[k]==t[j]) _next[++j]=++k; else k=_next[k]; } } int kmp_pos() { int i=0,j=0; get_next(); while(i<slen&&j<tlen) { if(j==-1||s[i]==t[j])//如果匹配没出问题,直接匹配 { i++;j++; } else//如果匹配出现了问题,那么直接跳转到next[j]来,因为next[j]等于当前最长的公共前后缀,所以在公共后缀前面的那一部分没必要匹配了; j=_next[j]; } if(j==tlen) return i-tlen; return -1; } int main() { int tt; scanf("%d",&tt); while(tt--) { scanf("%d%d",&slen,&tlen); fill(_next,_next+tlen,0); for(int i=0;i<slen;i++) scanf("%d",&s[i]); for(int i=0;i<tlen;i++) scanf("%d",&t[i]); if(kmp_pos()==-1) cout<<-1<<endl; else cout<<kmp_pos()+1<<endl; } }
C - Period
题意:给你一个字符串,问你当前长度i的前缀是否是循环字符串,是的话输出循环节的次数
解题思路:当前i的最小循环节为i-next[i],算出当前的next数组就行了
代码
#include<algorithm> #include<cstring> #include<cstdio> #include<queue> #include<iostream> #include<string> using namespace std; const int maxn=1e6+100; char t[maxn]; int _next[maxn],tlen; void get_next() { fill(_next,_next+tlen,0); int j=0,k=-1;_next[0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) _next[++j]=++k; else k=_next[k]; } } int main() { int tt=0; while(scanf("%d",&tlen)!=EOF) {if(tlen==0) break; tt++; scanf("%s",t); get_next();cout<<"Test case #"<<tt<<endl; for(int i=1;i<=tlen;i++) { int t=i-_next[i]; if(i%t==0&&i/t!=1) { cout<<i<<" "<<i/t<<endl; } } cout<<endl; } }
题意:求最小循环节出现的次数
解题思路:最小循环节=i-next[i];
代码
#include<algorithm> #include<cstring> #include<cstdio> #include<queue> #include<iostream> #include<string> using namespace std; const int maxn=1e6+100; char t[maxn]; int _next[maxn],tlen; void get_next() { fill(_next,_next+tlen,0); int j=0,k=-1;_next[0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) _next[++j]=++k; else k=_next[k]; } } int main() { int tt=0; while(scanf("%d",&tlen)!=EOF) {if(tlen==0) break; tt++; scanf("%s",t); get_next();cout<<"Test case #"<<tt<<endl; for(int i=1;i<=tlen;i++) { int t=i-_next[i]; if(i%t==0&&i/t!=1) { cout<<i<<" "<<i/t<<endl; } } cout<<endl; } }
E - Count the string
题意:给你一个字符串,问你前缀出现的次数和
解题思路:dp+kmp,dp[i]表示当前字符子串包含的前缀出现的次数和
代码
void get_next() { int j=0,k=-1;_next[0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) { _next[++j]=++k; } else k=_next[k]; } } int main() { scanf("%d",&tt); while(tt--) { memset(_next,0,sizeof(_next)); memset(dp,0,sizeof(dp)); scanf("%d",&tlen); scanf("%s",t); get_next(); for(int i=1;i<=tlen;i++) dp[i]=1; for(int i=1;i<=tlen;i++) { if(_next[i]==0) continue; else dp[i]=dp[i]+dp[_next[i]]; dp[i]%=mod; } int ans=0; for(int i=1;i<=tlen;i++) { ans+=dp[i];ans%=mod; } cout<<ans<<endl; } }
F - Cyclic Nacklace
题意:问你补充多少个字符,可以使得这个字符串变成循环串
解题思路:找到最小循环节,然后len%(最小循环节)
代码
#include<algorithm> #include<cstring> #include<cstdio> #include<queue> #include<string> #include<iostream> using namespace std; const int maxn=2e5+100; int n,tlen; int _next[maxn]; char t[maxn]; void get_next() { int j,k; j=0;k=-1;_next[0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) _next[++j]=++k; else k=_next[k]; } } int main() { scanf("%d",&n); while(n--) { scanf("%s",t);tlen=strlen(t); get_next(); int tmp=tlen-_next[tlen]; if(tlen%tmp==0) { if((tlen/tmp)!=1) cout<<0<<endl; else cout<<tlen<<endl; } else { cout<<tmp-tlen%tmp<<endl; } } }
G - Simpsons’ Hidden Talents
题意:给你两个字符串,问你最长的相同前后缀
解题思路:一直匹配下去,看到最后j的值决定
代码
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; const int maxn=1e5+100; char s[maxn],t[maxn]; int _next[maxn],slen,tlen; void get_next() { int j=0,k=-1;_next[0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) _next[++j]=++k; else k=_next[k]; } } void pre() { int i=0,j=0; get_next(); while(i<slen) { if(j==-1||s[i]==t[j]) { i++;j++; } else j=_next[j]; } if(j==0) cout<<j<<endl; else { for(int k=0;k<j;k++) cout<<t[k]; cout<<" "<<j<<endl; } } int main() { while(scanf("%s %s",t,s)!=EOF) { tlen=strlen(t);slen=strlen(s); pre(); } }
H - Milking Grid
题意:给你一个矩阵字符串,找到最小的矩阵循环节
解题思路:首先求出行的每行最小循环节最大值,再求出每列的最小循环节最大值,这两个值相乘就是答案
代码
#include<algorithm> #include<cstring> #include<cstdio> #include<queue> #include<iostream> using namespace std; int n,m,tlen; char s[10050][80]; char t[10050]; int _next[10050][80],_next2[80][10050]; void get_next(int pos,int flag) { int j,k; j=0;k=-1; if(flag==0) { _next[pos][0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) _next[pos][++j]=++k; else k=_next[pos][k]; } } else if(flag==1) { _next2[pos][0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) _next2[pos][++j]=++k; else k=_next2[pos][k]; } } } int main() { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%s",s[i]); int maxxc=-1; int maxxr=-1; for(int i=0;i<n;i++) { tlen=m; for(int j=0;j<m;j++) t[j]=s[i][j]; get_next(i,0); int tmp=tlen-_next[i][tlen]; maxxc=max(maxxc,tmp); } for(int j=0;j<m;j++) { tlen=n; for(int i=0;i<n;i++) t[i]=s[i][j]; get_next(j,1); int tmp=tlen-_next2[j][tlen]; maxxr=max(maxxr,tmp); } cout<<maxxr*maxxc<<endl; }
题意:给你一个字符串,找一个最长子串,既是前缀,又是后缀,还出现在前后缀中间;
解题思路:先从前后缀开始找起,用next数组一直递推,知道相同前后缀的长度不超过字符串长度的一半,标记,然后,暴力匹配找出中间的
代码
#include<algorithm> #include<cstring> #include<cstdio> #include<string> #include<iostream> using namespace std; const int maxn=1e6+100; char t[maxn]; int flag[maxn]; int _next[maxn]; int tlen,n; void get_next() { int j=0,k=-1; _next[0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) _next[++j]=++k; else k=_next[k]; } } int main() { scanf("%d",&n); while(n--) { memset(flag,0,sizeof(flag)); memset(_next,0,sizeof(_next)); scanf("%s",t); tlen=strlen(t); get_next(); int tmp=tlen; while(tmp>0) { if(tmp*2<=tlen) flag[tmp]=1; tmp=_next[tmp]; } int ans=0; for(int i=tlen-1;i>=0;i--) { tmp=i; while(tmp>0) { if(flag[tmp]&&tmp*2<=i&&i+tmp<=tlen) { ans=max(ans,tmp); } tmp=_next[tmp]; } } cout<<ans<<endl; } }
题意:给你两个字符串,问你第二个串的每一个后缀在第一个串出现的次数
解题思路:把两个串反转,就变成了求前缀的问题,用一个数组cnt计算每次匹配的位置出现的次数,然后反着递推一边
代码
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; typedef long long ll; const ll mod=1e9+7; const int maxn=1e6+100; char s[maxn]; char t[maxn]; int _next[maxn]; int slen,tlen; ll sum; ll cnt[maxn]; void get_next() { memset(_next,0,sizeof(_next)); int j,k;//k代表的是相同字符的长度; j=0;k=-1;_next[0]=-1; while(j<tlen)//计算模式串的next数组,也就是每个字符若是匹配失败,该跳的位置; { if(k==-1||t[j]==t[k])//如果相等等于之前的+1; _next[++j]=++k; else k=_next[k];//如果不相等,跳转到k=next[k]去;把指针转移到之前的相同串去,保证最大前缀 } } int kmp_count() { ll ans=0,i=0,j=0; if(slen==1&&tlen==1) { if(s[0]==t[0]) return 1; else return 0; } get_next(); while(i<slen) { if(j==-1||s[i]==t[j]) { i++;j++; } else j=_next[j]; cnt[j]++; if(j==tlen) { j=_next[j]; } } for(int i=tlen;i>0;i--) { cnt[_next[i]]=cnt[_next[i]]+cnt[i]; } for(int i=1;i<=tlen;i++) ans+=(i*cnt[i])%mod; ans%=mod; return ans; } int main() { int tt; scanf("%d",&tt); while(tt--) { memset(cnt,0,sizeof(cnt)); scanf("%s%s",s,t); tlen=strlen(t);slen=strlen(s); reverse(s,s+slen);reverse(t,t+tlen); cout<<kmp_count()<<endl; } }
K - Bazinga
题意:给你n和字符串,让你求出一个字符串,他前面的字符串中,存在不是他的子串的字符串,输出这个字符串的位置,多个就输出最晚出现的那个;
解题思路:暴力求会超时,所以用一个标记数组,标记之前成功匹配的字符串,因为之前成功过,说明后来的串更大,直接匹配当前串与更大的串,失败就GG,成功说明标记串不用匹配了,因为是子串的子串,也是子串
代码
#include<algorithm> #include<cstring> #include<cstdio> #include<iostream> using namespace std; char a[550][2050]; int tlen,slen; int s[2050],t[2050]; int _next[2050]; int visit[550]; void get_next(int x,int y) { memset(_next,0,sizeof(_next)); int j,k;//k代表的是相同字符的长度; j=0;k=-1;_next[0]=-1; tlen=strlen(a[x]); slen=strlen(a[y]); while(j<tlen)//计算模式串的next数组,也就是每个字符若是匹配失败,该跳的位置; { if(k==-1||a[x][j]==a[x][k])//如果相等等于之前的+1; _next[++j]=++k; else k=_next[k];//如果不相等,跳转到k=next[k]去;把指针转移到之前的相同串去,保证最大前缀 } } int kmp_pos(int x,int y) { int i=0,j=0; get_next(x,y); while(i<slen&&j<tlen) { if(j==-1||a[y][i]==a[x][j])//如果匹配没出问题,直接匹配 { i++;j++; } else//如果匹配出现了问题,那么直接跳转到next[j]来,因为next[j]等于当前最长的公共前后缀,所以在公共后缀前面的那一部分没必要匹配了; j=_next[j]; if(j==tlen) return i-tlen; } return -1; } int main() { int tt; int n; int cot=0; scanf("%d",&tt); while(tt--) { memset(visit,0,sizeof(visit)); cot++; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%s",a[i]); int flag=0; int ans=-1; for(int i=1;i<=n;i++) { for(int j=1;j<=i-1;j++) { if(visit[j]==1) continue; if(kmp_pos(j,i)==-1) ans=i; else visit[j]=1; } } cout<<"Case #"<<cot<<": "; cout<<ans<<endl; } }
L - Blue Jeans
题意:给你n个长度都为60的母串,让你求出这些母串中最长的公共子串
解题思路:暴力。。。找一个母串,暴力他所有的子串进行kmp就行了
代码
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; char a[15][100]; char t[100]; char s[100]; char char_ans[100]; int next[100]; int slen,tlen; bool bj(char x[],char y[],int w) { for(int i=0;i<w;i++) { if(x[i]==y[i]) continue; if(x[i]>y[i]) { return true; } } return false; } void get_next() { int j=0,k=-1;next[0]=-1; while(j<tlen) { if(k==-1||t[k]==t[j]) next[++j]=++k; else k=next[k]; } } int kmp_pos() { int i=0,j=0; get_next(); while(i<slen&&j<tlen) { if(j==-1||s[i]==t[j]) { i++;j++; } else j=next[j]; } if(j==tlen) return i-tlen; else return -1; } int main() { int tt,n; int ans=-1; int cnt; int flag; scanf("%d\n",&tt); while(tt--) { ans=-1; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",a[i]); } for(int i=0;i<=59;i++)//从位置i开始 { cnt=0;tlen=0; for(int j=i;j<=59;j++)//到位置j结束 { slen=60; t[cnt]=a[1][j];tlen++;cnt++; flag=0; for(int k=2;k<=n;k++)//循环n个串找字串; { for(int l=0;l<60;l++)//s串的赋值; s[l]=a[k][l]; int x=kmp_pos(); if(x==-1) flag=1; } if(flag==0) { if(tlen==ans) { if(bj(t,char_ans,tlen)) { for(int k=0;k<tlen;k++) char_ans[k]=t[k]; } } else if(tlen>ans) { ans=tlen; for(int k=0;k<tlen;k++) char_ans[k]=t[k]; } } } } if(ans<3) printf("no significant commonalities\n"); else { for(int i=0;i<ans;i++) printf("%c",char_ans[i]); printf("\n"); } } }
M - Substring Frequency
题意:两个串,求第二个串出现的次数
解题思路:模板题
代码
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn=1e6+100; char s[maxn],t[maxn]; int _next[maxn]; int slen,tlen; void get_next() { int j,k;//k代表的是相同字符的长度; j=0;k=-1;_next[0]=-1; while(j<tlen)//计算模式串的next数组,也就是每个字符若是匹配失败,该跳的位置; { if(k==-1||t[j]==t[k])//如果相等等于之前的+1; _next[++j]=++k; else k=_next[k];//如果不相等,跳转到k=next[k]去;把指针转移到之前的相同串去,保证最大前缀 } } int kmp_pos() { int i=0,j=0; get_next(); while(i<slen&&j<tlen) { if(j==-1||s[i]==t[j])//如果匹配没出问题,直接匹配 { i++;j++; } else//如果匹配出现了问题,那么直接跳转到next[j]来,因为next[j]等于当前最长的公共前后缀,所以在公共后缀前面的那一部分没必要匹配了; j=_next[j]; } if(j==tlen) return i-tlen; return -1; } int kmp_count() { int ans=0,i=0,j=0; if(slen==1&&tlen==1) { if(s[0]==t[0]) return 1; else return 0; } get_next(); for(i=0;i<slen;i++) { while(j>0&&s[i]!=t[j]) j=_next[j]; if(s[i]==t[j]) j++; if(j==tlen) { ans++; j=_next[j]; } } return ans; } int main() { int tt; scanf("%d",&tt); int cot=0; while(tt--) { cot++; scanf("%s %s",s,t); slen=strlen(s); tlen=strlen(t); cout<<"Case "<<cot<<": "; cout<<kmp_count()<<endl; } }
N - Making Huge Palindromes
题意:问你补充多少个字符+上原先的字符=一个回文串,输出回文串的最小长度
解题思路:将给的字符串反过来成为一个新串,求这两个串的最长公共前后缀就行了
代码
#include<algorithm> #include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<string> using namespace std; const int maxn=2e6+200; char s[maxn]; char t[maxn]; int _next[maxn]; int tlen,n,slen; void get_next() { int j,k;//k代表的是相同字符的长度; j=0;k=-1;_next[0]=-1; while(j<tlen)//计算模式串的next数组,也就是每个字符若是匹配失败,该跳的位置; { if(k==-1||t[j]==t[k])//如果相等等于之前的+1; _next[++j]=++k; else k=_next[k];//如果不相等,跳转到k=next[k]去;把指针转移到之前的相同串去,保证最大前缀 } } int kmp_pos() { int i=0,j=0; get_next(); while(i<slen&&j<tlen) { if(j==-1||s[i]==t[j])//如果匹配没出问题,直接匹配 { i++;j++; } else//如果匹配出现了问题,那么直接跳转到next[j]来,因为next[j]等于当前最长的公共前后缀,所以在公共后缀前面的那一部分没必要匹配了; j=_next[j]; } return slen*2-j; } int main() { int tt; int cot=0; scanf("%d",&tt); while(tt--) { scanf("%s",s); cot++; int cnt=0; slen=strlen(s); for(int i=slen-1;i>=0;i--) { t[cnt++]=s[i]; } tlen=cnt; cout<<"Case "<<cot<<": "; cout<<kmp_pos()<<endl; } }
O - Unlucky Strings
题意:给你一个合法字符集,一个长度len,一个字符串z,问你字符串长度为len且由合法字符组成且不包括字符串z的所有字符串的数量
解题思路:首先要确定是矩阵构造的题目,构造一个矩阵ans【i】【j】,枚举当前正在匹配的字符的位置i,然后枚举字符集中的所有字符c,这一步代表的含义就是匹配到第i个位置时遇到字符c 要跳到哪个位置j,然后将ans[i][j]++,然后就是矩阵快速幂
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=55; char s[maxn],t[maxn]; ll n,tlen,slen; ll _next[maxn]; struct Matrix { unsigned m[maxn][maxn]; Matrix() { memset(m,0,sizeof(m)); } void init() { for(int i=0; i<maxn; i++) for(int j=0; j<maxn; j++) m[i][j]=(i==j); } Matrix operator +(const Matrix &b)const { Matrix c; for(int i=0; i<maxn; i++) { for(int j=0; j<maxn; j++) { c.m[i][j]=(m[i][j]+b.m[i][j]); } } return c; } Matrix operator *(const Matrix &b)const { Matrix c; for(int i=0; i<maxn; i++) { for(int j=0; j<maxn; j++) { for(int k=0; k<maxn; k++) { c.m[i][j]=(c.m[i][j]+(m[i][k]*b.m[k][j])); } } } return c; } Matrix operator^(const ll &t)const { Matrix ans,a=(*this); ans.init(); ll n=t; while(n) { if(n&1) ans=ans*a; a=a*a; n>>=1; } return ans; } }; void get_next() { int j=0,k=-1;_next[0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) _next[++j]=++k; else k=_next[k]; } } int main() { int tt;int cot=0; scanf("%d",&tt); while(tt--) { cot++; scanf("%lld",&n); scanf("%s%s",s,t); tlen=strlen(t); slen=strlen(s); get_next(); Matrix a; for(int i=0;i<tlen;i++) { for(int j=0;j<slen;j++) { int tmp=i; while(tmp&&t[tmp]!=s[j]) tmp=_next[tmp]; if(t[tmp]==s[j]) tmp++; a.m[i][tmp]++; } } /* for(int i=0;i<=tlen;i++) { for(int j=0;j<=tlen;j++) { cout<<a.m[i][j]<<" "; } cout<<endl; }*/ a=a^n; unsigned ans=0; for(int i=0;i<tlen;i++) ans+=a.m[0][i]; cout<<"Case "<<cot<<": "; cout<<ans<<endl; } }