题意:
给一个字符串 s s s ,长度为 n n n ,问是否存在一个 k k k ,满足 k ∣ n k|n k∣n ,并且将 s s s 分成相等的 k k k 段子串,每一段子串为循环同构,两个串 s 1 , s 2 s1,s2 s1,s2 循环同构就是满足存在一个 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 <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define PII pair<int,int>
#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 <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#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;
}