【BZOJ】2795: [Poi2012]A Horrible Poem

题意

一个长度为\(n(n \le 500000)\)的字符串\(s\),给\(q(q \le 2000000)\)个询问,每个询问给一个区间\([l, r]\),求这个区间内最短的循环节。

分析

分析以下可以知道:

  1. 假设循环节长度为\(len\),则\(s[l, r-len]=s[l+len, r]\)
  2. \(len|(r-l+1)\)
  3. 如果\(len\)是循环节,则\(len * p\)也是循环节\((len * p|(r-l+1))\)

题解

首先判两个串是否相等用hash即可。
根据\(1, 2\)我们很容易得到\(O(qn^{0.5})\)的做法。
可是由于性质\(3\)的存在,我们先分解\((r-l+1)\)的质因数,然后依次考虑删去质因数即可。
复杂度\(O(qlogn)\)

#include <bits/stdc++.h>
using namespace std;
inline int getint() {
	int x=0, c=getchar();
	for(; c<48||c>57; c=getchar());
	for(; c>47&&c<58; x=x*10+c-48, c=getchar());
	return x;
}
const unsigned int N=500005, mo=1e9+7;
int p[N], cnt, m[N], bg[N];
unsigned int a[N], po[N];
void init(int n) {
	for(int i=2; i<=n; ++i) {
		if(!bg[i]) {
			bg[i]=i;
			p[cnt++]=i;
		}
		for(int j=0, t; j<cnt; ++j) {
			t=p[j]*i;
			if(t>n) {
				break;
			}
			bg[t]=p[j];
			if(i%p[j]==0) {
				break;
			}
		}
	}
}
inline bool check(int a, int b, int c, int d) {
	return m[b]-m[a-1]*po[b-a+1]==m[d]-m[c-1]*po[d-c+1];
}
int main() {
	int n=getint();
	init(n);
	po[0]=1;
	for(int i=1; i<=n; ++i) {
		m[i]=m[i-1]*mo+getchar();
		po[i]=po[i-1]*mo;
	}
	int q=getint();
	for(; q--; ) {
		int l=getint(), r=getint(), len=r-l+1;
		for(int x=len; x>1; ) {
			int y=bg[x];
			for(; len%y==0 && check(l, r-len/y, l+len/y, r); len/=y);
			for(; x%y==0; x/=y);
		}
		printf("%d\n", len);
	}
	return 0;
}
posted @ 2015-11-22 13:56  iwtwiioi  阅读(319)  评论(0编辑  收藏  举报