2020 杭电多校8 1009 Isomorphic Strings (哈希/kmp)

题意:

给一个字符串 s s s ,长度为 n n n ,问是否存在一个 k k k ,满足 k ∣ n k|n kn ,并且将 s s s 分成相等的 k k k 段子串,每一段子串为循环同构,两个串 s 1 , s 2 s1,s2 s1s2 循环同构就是满足存在一个 i i i ,使得 s 2 = s 1 i + 1 s 2 i + 2 s 3 i + 3 . . . s 1 1 s 1 2 . . . s 1 i s2=s1_{i+1}s2_{i+2}s3_{i+3}...s1_1s1_2...s1_i s2=s1i+1s2i+2s3i+3...s11s12...s1i

思路:

考虑枚举所有 n n n 的因数 k k k ,对于每一个 k k k ,计算出每一个子串的哈希值集合,并且求出第一个子串的所有循环同构的哈希值集合,若第一个集合为第二个的子集则 k k k 合法。在判断是可以使用数组来记录,并且使用一个时间戳来标记,不用每次清空。
或者使用 k m p kmp kmp 来解决这个问题,对于每一个 k k k 进行判断的时候,可以将第一个长度为 n / k n/k n/k 的子串复制一遍并接在后面作为 t t t ,可以知道 t t t 中包含所有的循环同构,对于后面的每 n / k n/k n/k 的子串都进行与 t t t 进行匹配,使用 k m p kmp kmp 即可,一次判断的复杂度为 O ( n ) O(n) O(n)

代码:

法一:

#include 
#define ll long long
#define inf 0x3f3f3f3f
#define PII pair
#define ls x<<1
#define rs x<<1|1
#define fi first
#define se second
#define pb push_back
#define cwk freopen("D:\\c++\\in.txt","r",stdin),freopen("D:\\c++\\out.txt","w",stdout)
using namespace std;
const int mod=1e7+7;
const int base=1331;
const int N=5e6+10;
int T;
int n;
char s[N];
int p[N],a[N];
int jl[mod];
int js(int l,int r) { // 子串哈希值
	return (a[r]-1ll*a[l-1]*p[r-l+1]%mod+mod)%mod;
}
int tot=0; //时间戳
bool pd(int k,int id) {
	if(k==1) return 0;
	int len=n/k;
	int l=1,r=len;
	jl[js(1,len)]=id;
	for(int i=1;i<len;i++) {
		int temp1=(1ll*js(i+1,len)*p[i]%mod+js(1,i))%mod;
		jl[temp1]=id;
	}
	for(int i=1;i<=k;i++) {
		int temp=js(l,r);
		if(jl[temp]!=id) return 0; // 不是子集
		l+=len,r+=len;
	}
	return 1;
}
int main()
{
	//cwk;
	scanf("%d",&T);
	p[0]=1;
	for(int i=1;i<N;i++) p[i]=(1ll*p[i-1]*base)%mod;
	while(T--) {
		scanf("%d",&n);
		scanf("%s",s+1);
		for(int i=1;i<=n;i++) {
			a[i]=(1ll*a[i-1]*base%mod+(s[i]-'a'+1))%mod;
		}
		int flag=0;
		for(int i=1;1ll*i*i<=1ll*n&&!flag;i++) { // 根号枚举因数
			if(n%i==0) {
				flag|=pd(i,++tot);
				flag|=pd(n/i,++tot);
			}
		}
		if(!flag) puts("No");
		else puts("Yes");
	}

	return 0;
}

法二:

#include 
#define ll long long
#define PII pair
#define ls x<<1
#define rs x<<1|1
#define fi first
#define se second
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
const int N=5e6+10;
int T,n;
char s[N];
int nxt[N];
bool kmp(char *t,char *s) {
	int ans=0;
	int i,j;
	int len1=strlen(t+1),len2=strlen(s+1);
	nxt[0]=nxt[1]=0;
	for(i=2,j=0;i<=len1;i++){
		while(j&&t[j+1]!=t[i])j=nxt[j];
		if(t[j+1]==t[i])++j;
		nxt[i]=j;
	}
	for(i=1,j=0;i<=len2;i++){
		while(j&&t[j+1]!=s[i])j=nxt[j];
		if(t[j+1]==s[i])++j;
		if(j==len1){
			ans++;j=nxt[j];
			return 1;
		}
	}
	return 0;
}
char a[N],b[N];
bool pd(int k) {
	if(k==1) return 0;
	int len=n/k;
	for(int i=1;i<=len;i++) {
		a[i]=s[i];
		a[i+len]=s[i];
	}
	a[2*len+1]='\0';
	for(int i=len;i<=n;i+=len) {
		for(int j=1;j<=len;j++) {
			b[j]=s[i+j];
		}
		b[len+1]='\0';
		if(kmp(b,a)==0) return 0;
	}
	return 1;
	
}
int main()
{
	//cwk;
	scanf("%d",&T);
	while(T--) {
		scanf("%d",&n);
		scanf("%s",s+1);
		int flag=0;
		for(int i=1;1ll*i*i<=n&&!flag;i++) {
			if(n%i==0) {
				flag|=pd(i);
				flag|=pd(n/i);
			}
		}
		if(!flag) puts("No");
		else puts("Yes");
	}

	return 0;
}

你可能感兴趣的:(比赛题解,HDU)