BZOJ4048 SDOI015 双旋转字符串 Hash
题意:给定两个字符串集合S T,每个集合内的所有字符串长度均相同,求有多少对字符串<Si,Tj>使得两个字符串拼接后,左右两半满足双旋转性质(一个字符串按照任意一个位置旋转后与另一个字符串重合)
题解:把较小的集合Hash后,暴力枚举另一个集合内的字符串。
#include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #include <map> using namespace std; #define ll long long #define Base 233 const int MAXN=4000000+2; int N,M,TS,TT; ll Hash,f[MAXN],p[MAXN],Ans; string S[MAXN],T[MAXN]; map<ll,int> m; ll Calc(int x,int y){ if(x>y) return 0; return f[y]-f[x-1]*p[y-x+1]; } int main(){ cin >> TS >> TT >> N >> M; if(TS<TT){ for(int i=1;i<=TS;i++) cin >> S[i]; for(int i=1;i<=TT;i++) cin >> T[i]; } else{ for(int i=1;i<=TS;i++) cin >> T[i]; for(int i=1;i<=TT;i++) cin >> S[i]; swap(N,M),swap(TS,TT); } p[0]=1; for(int i=1;i<=N+M;i++) p[i]=p[i-1]*Base; for(int i=1;i<=TS;i++){ for(int j=0;j<N;j++) Hash=Hash*Base+S[i][j]-'a'+1; m[Hash]++,Hash=0; } for(int i=1,Mid=(N+M)>>1;i<=TT;i++,Hash=0){ for(int j=0;j<2*Mid;j++) f[j+1]=f[j]*Base+T[i][j%Mid]-'a'+1; for(int j=Mid;j<M;j++) Hash=Hash*Base+T[i][j]-'a'+1; for(int j=1;j<=Mid;j++) if(Calc(j,j+M-Mid-1)==Hash) Ans+=m[Calc(j+M-Mid,j+Mid-1)]; } cout << Ans << endl; return 0; }