1. 程式人生 > 實用技巧 >hdu 6863 Isomorphic Strings 雜湊+求公因子

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