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;
}

 

posted @ 2020-08-15 14:57  kongbursi  阅读(187)  评论(0编辑  收藏  举报