2020杭电多校第八场 1009 Isomorphic Strings
原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6863
Isomorphic Strings
Time Limit: 8000/8000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 0 Accepted Submission(s): 0
Problem Description
It is preferrable to read the pdf statment.
Two strings are called cyclical isomorphic if one can rotate one string to get another one. 'Rotate' here means ''to take some consecutive chars (maybe none) from the beginning of a string and put them back at the end of the string in the same order''. For example, string ''abcde'' can be rotated to string ''deabc''.
Now that you know what cyclical isomorphic is, Cuber QQ wants to give you a little test.
Here is a string s of length n. Please check if s is a concatenation of k strings, s1,s2,⋯,sk (k>1), where,
Print ''Yes'' if the check is positive, or ''No'' otherwise.
Two strings are called cyclical isomorphic if one can rotate one string to get another one. 'Rotate' here means ''to take some consecutive chars (maybe none) from the beginning of a string and put them back at the end of the string in the same order''. For example, string ''abcde'' can be rotated to string ''deabc''.
Now that you know what cyclical isomorphic is, Cuber QQ wants to give you a little test.
Here is a string s of length n. Please check if s is a concatenation of k strings, s1,s2,⋯,sk (k>1), where,
- k is a divisor of n;
- s1,s2,…,sk are of equal length: nk;
- There exists a string t, which is cyclical isomorphic with si for all 1≤i≤k.
Print ''Yes'' if the check is positive, or ''No'' otherwise.
Input
The first line contains an integer T (1≤T≤1000), denoting the number of test cases. T cases follow.
It is guaranteed that the sum of n does not exceed 2⋅107.
- The first line of each test case contains an integer n (1≤n≤5⋅106).
- The second line contains a string s of length n consists of lowercase letters only.
It is guaranteed that the sum of n does not exceed 2⋅107.
Output
For each test case, output one line containing ''Yes'' or ''No'' (without quotes).
Sample Input
6
1
a
2
aa
3
aab
4
abba
6
abcbcc
8
aaaaaaaa
Sample Output
No
Yes
No
Yes
No
Yes
题意:给你一个长度为n的字符串s,让你判断是否存在一个k,使字符串分为连续的n/k个子串,要求子串之间两两互为循环同构。
思路:
虽然时间限制高达8000ms,但考虑到数据较大,所以完全暴力做可能会T(但不少人七千多毫秒,接近八千毫秒卡过了),因此考虑减枝。
既然要把字符串分为n/k个子串,那么每个子串中都要包含原s串里出现过的每种字符,因此,统计s串中不同种类字符的个数,取他们共同的最大公因数mi,
再枚举mi的因数作为子串个数(这样就保证了每种字符都平均的分配到每个子串中,这样子串两两之间才有互为循环同构的可能),
然后利用kmp算法判断n/k个子串是否两两之间为循环同构(以第一个子串为主串),注意k>1与字符种类数为1的情况要特判。
代码:
#include<bits/stdc++.h> #define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0) #define multiCase int TT;scanf("%d",&TT);for(int tt=1;tt<=TT;tt++) #define rep(i,a,b) for(int i=a;i<=b;i++) #define repp(i,a,b) for(int i=a;i<b;i++) #define per(i,a,b) for(int i=a;i>=b;i--) #define perr(i,a,b) for(int i=a;i>b;i--) #define pb push_back #define eb push_back #define mst(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; const int INF=0x3f3f3f3f; const ll LINF=0x3f3f3f3f3f3f3f3f; const double eps=1e-12; const double PI=acos(-1.0); const double angcst=PI/180.0; const ll mod=998244353; ll max_3(ll a,ll b,ll c){if(a>b&&a>c)return a;if(b>c)return b;return c;} ll min_3(ll a,ll b,ll c){if(a<b&&a<c)return a;if(b<c)return b;return c;} ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);} ll lcm(ll a,ll b){return a/gcd(a,b)*b;} ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;} ll qmul(ll a,ll b){ll s=(long double)a/mod*b;s=a*b-s*mod;if(s<0)s+=mod;if(s>=mod)s-=mod;return s;} const int maxn=1000010; string str; int n,f,t,cnt; map<int,int>mp,vis; void kmp(string S,string T) { int Slen,Tlen,Next[T.length()+10]; Slen=S.length(),Tlen=T.length(); int i,j=-1; Next[0]=-1; for(i=1;i<Tlen;i++){ while(j>-1&&T[j+1]!=T[i]) j=Next[j]; if(T[j+1]==T[i]) j++; Next[i]=j; } j=-1; for(i=0;i<Slen;i++){ while(j>-1&&T[j+1]!=S[i]) j=Next[j]; if(T[j+1]==S[i]) j++; if(j==Tlen-1) { cnt++;//成功匹配 return; } } return; } int main() { multiCase { int mi; f=0,t=0; mp.clear(); vis.clear(); scanf("%d",&n); cin>>str; if(n==1) { printf("No\n"); continue; } rep(i,0,n-1) { mp[str[i]]++; if(!vis[str[i]]) { vis[str[i]]=1; t++; } } if(t==1) { printf("Yes\n"); continue; } int p=1; for(auto &it:mp) { int i=it.second; if(p==1) { mi=i; } else { mi=gcd(mi,i); if(mi==1)break;//如果最大公因数为1后面的便无须继续判断了 } p++; } rep(i,2,mi)//i表示子串个数,n/i则表示每个子串的长度 { if(mi%i)continue;//不是因数直接跳过 string s=str.substr(0,n/i); s=s+s; cnt=0;//表示成功匹配的子串的个数 rep(j,2,i) { kmp(s,str.substr((j-1)*(n/i),n/i)); } if(cnt==i-1)//因为把第一个子串作为主串了,所以是i-1 { f=1; break; } } if(f)printf("Yes\n"); else printf("No\n"); } return 0; }