[Poi2012]A Horrible Poem BZOJ2795

分析:

这是今天下午的考试题,推了2个小时,考试中A掉了

首先,循环串通过字符串hash可以O(1)判断:get_hash(l,r-len)==get_hash(l+len,r);显然可证。

我们其次可以发现,循环串的长度是所求串的长度的约数

之后我们可以发现,如果两个不同的子串是循环串,那么这两个子串长度的gcd也一定是循环串。

那么,我们就可以发现,将长度质因数分解,之后每次判断长度/这个质因子的所得长度能否构成循环串,如果能,就将长度除以这个质因子。

至于质因子,我们可以线筛...详情自己去分析,或者去看下面的代码...懒得讲了...

附上代码:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cstdlib>
using namespace std;
#define N 500005
#define Base 131
#define ll unsigned long long
char s[N];
ll hash[N],b[N];
int n,Q,ans[N],pri[N],vis[N],last[N],cnt;
ll get_hash(int x,int y)
{
	return hash[y]-hash[x-1]*b[y-x+1];
}
void init()
{
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			pri[++cnt]=i;
			last[i]=i;
		}
		for(int j=1;j<=cnt&&i*pri[j]<=n;j++)
		{
			vis[pri[j]*i]=1;last[pri[j]*i]=pri[j];
			if(!(i%pri[j]))break;
		}
	}
}
int que[N];
int main()
{
	//freopen("str.in","r",stdin);
	//freopen("str.out","w",stdout);
	scanf("%d",&n);
	init();
	scanf("%s",s+1);
	b[0]=1;
	for(int i=1;i<=n;i++)b[i]=b[i-1]*Base;
	for(int i=1;i<=n;i++)
	{
		hash[i]=hash[i-1]*Base+s[i]-'a';
	}
	scanf("%d",&Q);
	while(Q--)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		int len=b-a+1;int t=len,tot=0;
		if(len==1)
		{
			puts("1");
			continue;
		}
		while(t!=1)
		{
			que[++tot]=last[t];
			t=t/last[t];
		}
		for(int i=1;i<=tot;i++)
		{
			t=len/que[i];
			if(get_hash(a,b-t)==get_hash(a+t,b))len=len/que[i];
		}
		printf("%d\n",len);
	}
	return 0;
}

Orz O(n*sqrt(n))的时间也能过...暴力踩标程...

posted @ 2018-05-15 20:57  Winniechen  阅读(227)  评论(0编辑  收藏  举报