[BZOJ 2795]POI2012 A Horrible Poem

很优美的题目:)

列出以下性质:

1、循环节一定是长度的约数(废话。。。)

2、如果n是一个循环节,那么k*n也必定是一个循环节(关键所在)

3、n是[l,r]这一段的循环节  的充要条件是  [l,r-n]和[l+n,r]相同(利用这个性质我们在判断是否为循环姐是可以做到O(1))


我们从性质2开始,如果len是循环节,那么最小循环节一定是len的约数——是不是有想法了?

枚举所有的质因子,不断除以原长并保证其仍是循环节,直到不能再小为止。

复杂度O(nlogn)(常数很小)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int Maxn=500005, MaxP=(1e9)+7;
char ch;
int n,pow[Maxn],i,j,l,r,len,m;
int check[Maxn],prime[Maxn],hash[Maxn],tot;

int read(){
  char ch=getchar();
  int ret=0;
  while (ch<'0' || ch>'9') ch=getchar();
  while (ch>='0' && ch<='9')
    {ret=ret*10+ch-'0'; ch=getchar();}
  return ret;
}

void init(){
  for (i=2;i<Maxn;i++){
  	if (check[i]==0) check[i]=i, prime[++tot]=i;
  	for (j=1;j<=tot;j++){
  	  if (i*prime[j]>=Maxn) break;
  	  check[i*prime[j]]=prime[j];
  	  if (i%prime[j]==0) break;
  	}
  }
}

bool equal(int l1,int r1,int l2,int r2){
  int t1=hash[r1]-hash[l1-1]*pow[r1-l1+1];
  int t2=hash[r2]-hash[l2-1]*pow[r2-l2+1];
  return (t1==t2);
}

int main(){
  freopen("okr.in","r",stdin);
  freopen("okr.out","w",stdout);
  init();
  scanf("%d\n",&n);
  for (i=1,pow[0]=1;i<=n;i++)
    pow[i]=pow[i-1]*MaxP;
  for (i=1;i<=n;i++){
    ch=getchar();
    hash[i]=hash[i-1]*MaxP+ch;
  }
  m=read();
  while (m--){
  	l=read(); r=read();
  	len=r-l+1;
  	for (i=len;i>1;){
  	  j=check[i];
  	  while ( len%j==0 && equal(l,r-len/j,l+len/j,r) ) len/=j;
	  while (i%j==0) i=i/j;
    }
    printf("%d\n",len);
  }
  return 0;
}


你可能感兴趣的:(字符串,poi,hash,2012)