题目大意:给定一个字符串S,其长度为N,如果对于任意的i(1<=i<=n-t)有Si=Si+t(1<=t<n-1),那么称其满足性质g(T)。定义Per(S)为所有使得S满足性质g(T)的整数T的集合。多组数据,每次给一个S,要求你构造一个01串是的其PER集合与S相同
神TM构造题!!!!!
出题人脑洞太大了,这种题给tourist都不能现场做出来吧!!!(也不一定)
VFK的题解讲的很好,里面只有一个“显然”定理没有证,那我就来补充一下吧
本原串定理:如果一个串不是本原串,修改其中任意一个字符,它就会变成本原串。
反证法,假设都不是本原串,那么设第一个串的循环节长度为a,修改之后的循环节长度为b。
当gcd(a,b)=1时,我们可以找到任意一个没有修改的字符,然后根据这两种循环节就可以得出无论是原串还是修改之后的串所有字符均与此字符相同,而又因为事实上修改过一个字符,矛盾
当gcd(a,b)=d≠1时,设a=pd,b=qd,则可以在每个长度为d的小节内至少找到d-1个没被修改过的字符,如此也可以扩展到每个长度为d的小节中这些字符都是相同的,所以我们可以把这些字符都删掉,那又剩下长度为p和q的循环节,与上面的证明方法一样,此时gcd(p,q)=1
得证
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
using namespace std;
char s[N];
int nxt[N],n;
int P[N],cnt;
int a[N];
void KMP()
{
int i=1,j=0;
nxt[1]=0;
while(i<=n)
{
if(j==0||s[i]==s[j])
{
i++;j++;
nxt[i]=j;
}
else j=nxt[j];
}
}
int I,J;
void get()
{
while(1)
{
if(J==0||a[I]==a[J])
{
I++;J++;
nxt[I]=J;
break;
}
else J=nxt[J];
}
}
void gouzao()
{
int i,j,x,y;
I=2;J=1;
if(P[1]==1) a[1]=0;
else
{
a[1]=0;
for(i=2;i<P[1];i++) a[i]=0,get();
a[P[1]]=1,get();
}
for(i=2;i<=cnt;i++)
{
if(P[i]<=2*P[i-1])
{
for(j=P[i-1]+1;j<=P[i];j++)
a[j]=a[j-P[i]+P[i-1]],get();
}
else
{
for(j=P[i-1]+1;j<P[i]-P[i-1];j++)
a[j]=0,get();
int ii=I,jj=J;
a[P[i]-P[i-1]]=0;get();
if(nxt[I]!=1&&(P[i]-P[i-1])%(I-nxt[I])==0)
{
I=ii;J=jj;
a[P[i]-P[i-1]]=1;get();
}
for(j=P[i]-P[i-1]+1;j<=P[i];j++)
a[j]=a[j-P[i]+P[i-1]],get();
}
}
for(i=1;i<=n;i++)
printf("%d",a[i]);
puts("");
}
void doit()
{
scanf("%s",s+1);
n=strlen(s+1);
int i,j,x,y;
KMP();
i=n+1;
cnt=0;
while(i!=1)
{
cnt++;
P[cnt]=i-1;
i=nxt[i];
}
for(i=1;i<=cnt/2;i++)
swap(P[i],P[cnt+1-i]);
gouzao();
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
doit();
}