hdu 6863 Isomorphic Strings 哈希+求公因子
题意:
t组输入,每组数据输入一个整数n,代表字符串长度。下面再输入一个字符串
你需要判断这个字符串能不能分成大于1段,且这些段的最小表示法是一样的
例如:abccab,它可以分成2段,分别是abc和cab,它们都使用最小表示法(也就是字典序最小表示)表示之后都是abc,所以这两个串一样
题解:
因为你需要把字符串分成若干段,假设分成x段,那么x肯定满足n%x==0
这里就有两种方法,第一种就是枚举n的因子,然后再暴力去判断这个因子可不可以
另一种就是:假如存在一个可以满足题意的因子x(也就是说一段的长度为x,且每一段都相等),那么分成n/x段,每一段里面每一个字母的个数肯定要保持一样多
那么我们可以去找所有字母个数的最大公因子,设为ans,然后在1-ans里面暴力枚举就行。这样比去暴力枚举n的因子复杂度小
我们如何判断一个因子x可以不可满足题意?
对于一个串abccab,我们可以得出来a、b、c字母得个数都是2,那么它们和n的最大公因数就是2
那么长度为n的串,最多分成2段,每一段的长度n/2
然后我们开始暴力枚举[1,2)这个区间的因子,首先要判断一下n%x==0,不等于0这个因子就不行
这个枚举的是将几个n/2合并,例如1是满足n%x==0
这个时候abc为一段,cab为一段(如果x==2,那么就是将2个n/2合并成一段,即abccab为一段,因为题目要求必须将字符串分开,所以2不可以)
对于第一段abc,我们先标记一下abc的哈希值,然后再标记一下bca的哈希值,再标记cab的哈希值
然后再去判断后面的段的哈希值是否被标记过,如果标记过就没事,没标记过就证明这个因子不行
代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> #include<map> using namespace std; typedef unsigned long long ull; typedef long long ll; const int blo=13331; const int maxn = 5e6+5; ll xp[maxn],hash_1[maxn],num[maxn]; map<ll,ll>r; void init() { xp[0]=1; for(ll i=1; i<maxn; i++) xp[i]=xp[i-1]*blo; } ll make_hash(char str[],ll hash_[],ll len) { hash_[0]=0; for(ll i=1; i<=len; i++) { hash_[i]=hash_[i-1]*blo+(str[i]-'a'+1); //cout<<hash_[i]<<" "; } return len; } char str[maxn]; int main() { init(); ll t; scanf("%lld",&t); while(t--) { //r.clear(); ll n,flag=1; scanf("%lld",&n); scanf("%s",str+1); make_hash(str,hash_1,n); memset(num,0,sizeof(num)); for(ll i=1;i<=n;++i) { num[str[i]-96]++; } ll ans=n; for(ll i=1;i<=26;++i) //如果分组之后每组字符串最小表示法都一样,那么里面的每个字母的数量也一样,所以我们可以 { //以此来下手 if(num[i]) ans=__gcd(ans,num[i]); //这个ans里面放的就是最多你能分成多少组 } ll u=n/ans; //按照最大分成ans组,每一组的长度 if(u==1 && n!=1) { flag=0; } else { for(ll k=1;k<ans;++k) //枚举判断 { ll len=k*u; if(n%len) { continue; } r.clear(); flag=0; ll temp=hash_1[len]-hash_1[0]*xp[len]; r[temp]=1; for(ll i=1;i<=len;++i) //将一个字符串的各种类型字符串哈希值都算出来,并标记 { //例如字符串abc,你不仅要算出来abc的哈希值,还要算出来bca、cab的哈希值 temp=temp*blo+(str[i]-96); r[temp-hash_1[i]*xp[len]]=1; } for(ll j = 1; j * len <= n; j++) //判断后面几组是否和第一组一样 { if(r[hash_1[j*len]-hash_1[(j - 1)*len]*xp[len]]==0) { flag = 1; break; } } if (flag == 0) { break; } } } if(!flag) printf("Yes\n"); else printf("No\n"); } return 0; }