洛谷P5112 FZOUTSY(字符串哈希+莫队)

模板:后缀数组毒瘤

看见题面上的“后缀”、“$lcp$”等字样,dalao们大概马上想到后缀数组了。但是这道题能不能不用后缀数组呢?(其实是我不会后缀数组)

不难想到,$lcp\ge k$意味着两个字符串的前$k$个字符是相同的,于是大力字符串哈希,用$$(a_{1}x^{k-1}+a_{2}x^{k-2}+...+a_{k})*k-a_{1}x^{k}+a_{k+1}=a_{2}x^{k-1}+a_{3}x^{k-2}+...+a_{k+1}$$不难$O(n)$算出各个长度为$k$的子串的哈希值,接下来就是统计方案了。可以参考小Z的袜子,用莫队解决问题。

但是,$O(n\sqrt{n})$的莫队会被卡,所以要用块大小为$\frac{n}{\sqrt{m}}$的莫队,复杂度为$O(n\sqrt{m})$,对于$n^{2}m\le 10^{15}$,即$n\sqrt{m}\le 3.2*10^{7}$的数据,可以通过。

另外,模数太小的字符串哈希也会被卡,最好直接用unsigned long long,相当于模$2^{64}$,再对$n$个值离散化。

#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
#define PC(A) putchar(A)
using namespace std;
typedef unsigned long long ll;
const int N=3000050;
const int M=100050;
const int P=3000017;
const int x=233;  //233
char BB[1<<21],*S,*T;
inline char gc(){return S==T&&(T=(S=BB)+fread(BB,1,1<<20,stdin),S==T)?EOF:*S++;}
inline short rdc(){
    char c=gc();
    while(!islower(c))c=gc();
    return c-'a';
}
inline int rdi(){
    char c=gc();
    while(!isdigit(c))c=gc();
    int x=c&15;
    for(c=gc();isdigit(c);c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
short buf[25];
inline void wt(ll x){
    short l=-1;
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    PC(x|48);
    while(l>=0)PC(buf[l--]|48);
    PC('\n');
}
short a[N];
int h[P],nxt[N],p[N],cnt[N],l[M],r[M],t[M],n,k,sz,o=1;
ll v[N],s[M];
inline void add(ll w){
    int t=w%P;
    v[o]=w;
    nxt[o]=h[t];
    h[t]=o;
}
inline int find(ll w){
    int t=w%P,i;
    for(i=h[t];i;i=nxt[i])if(v[i]==w)return i;
    return 0;
}  //用哈希表实现离散化
inline ll fp(int p){  //快速幂
    ll a=x,s=1;
    while(p){
        if(p&1)s=s*a;
        a=a*a;
        p>>=1;
    }
    return s;
}
inline void work(){
    int i,t;
    ll tmp=fp(k),res=0;
    for(i=1;i<=k;i++)res=res*x+a[i];
    add(res);
    p[1]=1;
    for(i=k+1;i<=n;i++){
        res=res*x-a[i-k]*tmp+a[i];  //前文的式子
        if(t=find(res))p[i-k+1]=t;
        else{
            p[i-k+1]=++o;
            add(res);
        }
    }
}
int cmp(int a,int b){return (l[a]-1)/sz==(l[b]-1)/sz?r[a]<r[b]:l[a]<l[b];}
int main(){
    int m,i,x,y;
    ll ans=0;
    n=rdi();m=rdi();k=rdi();
    sz=n/sqrt(m);
    for(i=1;i<=n;i++)a[i]=rdc();
    work();
    for(i=0;i<m;i++){
        l[i]=rdi();r[i]=rdi();
        if(r[i]+k-1>n)r[i]=n-k+1;
        if(l[i]>r[i])l[i]=r[i];  //坑,小心这两种问题
        t[i]=i;
    }
    sort(t,t+m,cmp);
    x=l[t[0]];y=r[t[0]];
    for(i=x;i<=y;i++){
        ans+=cnt[p[i]];
        cnt[p[i]]++;
    }
    s[t[0]]=ans;
    for(i=1;i<m;i++){
        if(l[t[i]]<x)do{
            x--;
            ans+=cnt[p[x]];
            cnt[p[x]]++;
        }while(x>l[t[i]]);
        else for(;x<l[t[i]];x++){
            cnt[p[x]]--;
            ans-=cnt[p[x]];
        }
        if(r[t[i]]>y)do{
            y++;
            ans+=cnt[p[y]];
            cnt[p[y]]++;
        }while(y<r[t[i]]);
        else for(;y>r[t[i]];y--){
            cnt[p[y]]--;
            ans-=cnt[p[y]];
        }
        s[t[i]]=ans;
    }  //莫队
    for(i=0;i<m;i++)wt(s[i]);
    return 0;
}
View Code

 

posted @ 2019-07-28 14:15  wangyuchen  阅读(149)  评论(0)    收藏  举报