http://acm.hdu.edu.cn/showproblem.php?pid=6863
给一个字符串 s ,长度为 n ,问是否存在一个 k ,满足 k∣n ,将 s 分成相等的 k 段子串,每一段子串互为循环同构。其中,1≤T≤1000,1≤n≤5⋅106,∑n≤2⋅107。
考虑枚举所有 nn 的因数 kk ,对于每一个 k ,计算出每一个子串的哈希值集合,并且求出第一个子串的所有循环同构的哈希值集合,若第一个集合为第二个的子集则 kk 合法。在判断是可以使用数组来记录,并且使用一个时间戳来标记,不用每次清空。
或者使用 kmp来解决这个问题,对于每一个 k 进行判断的时候,可以将第一个长度为 n/k 的子串复制一遍并接在后面作为 tt,可以知道 tt 中包含所有的循环同构,对于后面的每 n/k的子串都进行与 t 进行匹配,使用 kmp 即可,一次判断的复杂度为 O(n)
#include
#define ll long long
#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()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
scanf("%s",s+1);
bool flag=false;
for(int i=1;i<=n&&!flag; i++)
{
if(n%i==0&&(pd(i)))//||pd(n/i)
{
flag = true;
}
}
if(!flag) puts("No");
else puts("Yes");
}
return 0;
}