BZOJ2528: [Poi2011]Periodicity

111 篇文章 0 订阅
14 篇文章 0 订阅

题目大意:给定一个字符串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();
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值