【POI2012】【BZOJ2795】A Horrible Poem

Description

给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节。
如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到。
Input

第一行一个正整数n (n<=500,000),表示S的长度。
第二行n个小写英文字母,表示字符串S。
第三行一个正整数q (q<=2,000,000),表示询问个数。
下面q行每行两个正整数a,b (1<=a<=b<=n),表示询问字符串S[a..b]的最短循环节长度。

Output

依次输出q行正整数,第i行的正整数对应第i个询问的答案。

Sample Input
8
aaabcabc
3
1 3
3 8
4 8

Sample Output
1
3
5
HINT

Source

鸣谢 jiangzoi&oimaster

如果只有一个询问,显然直接KMP就可以
多个询问就hash吧
枚举循环长度,循环长度为询问长度的约数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 500010
#define GET (ch>='0'&&ch<='9')
#define ULL unsigned long long
#define base 313
using namespace std;
char ch[MAXN];
int n,q;
int l,r,maxn;
int cnt[MAXN][30];
ULL Pow[MAXN],hash[MAXN];
int gcd(int a,int b)
{
    return !b?a:gcd(b,a%b);
}
void in(int &x)
{
    char ch=getchar();x=0;
    while (!GET)    ch=getchar();
    while (GET) x=x*10+ch-'0',ch=getchar();
}
void check(int l,int r,int num)
{
    int len=(r-l+1)-(r-l+1)/num;
    ULL A=hash[l+len-1]-Pow[len]*hash[l-1],B=hash[r]-hash[r-len]*Pow[len];
    if (A==B)   maxn=max(maxn,num);
}
void solve(int l,int r)
{
    int divisor=r-l+1;maxn=0;
    for (int i=0;i<26;i++)  divisor=gcd(divisor,cnt[r][i]-cnt[l-1][i]);
    for (int i=1;i*i<=divisor;i++)  
        if (divisor%i==0)   check(l,r,i),check(l,r,divisor/i);
}
int main()
{
    scanf("%d%s",&n,ch+1);Pow[0]=1;
    for (int i=1;i<=n;i++)  hash[i]=hash[i-1]*base+ch[i]-'a'+1,Pow[i]=Pow[i-1]*base;
    for (int i=1;i<=n;i++)
        for (int j=0;j<26;j++)  cnt[i][j]=cnt[i-1][j]+(ch[i]-'a'==j);
    in(q);
    for (int i=1;i<=q;i++)  in(l),in(r),solve(l,r),printf("%d\n",(r-l+1)/maxn);
}

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