【Codeforces 547E】Mike and Friends

后缀数组,二维数点

题目大意:

给定 $n$ 个字符串 $s_1,s_2,s_3,\dots,s_n$。每次询问给出 $l,r,k$,问 $s_k$ 在 $s_{l\sim r}$ 中的出现次数。

题解:

将所有串拼起来后求出后缀数组,记录每个位置属于哪个串。

对每个询问,我们 ST 表+二分,求出两个端点 $L,R$ 表示后缀数组上 $L\sim R$ 的位置代表的后缀包含该串。

然后就是找后缀数组上位置在 $L\sim R$ 内,且属于的串在 $l\sim r$ 的点的个数。

二维数点问题,离线树状数组即可。

时间复杂度 $O((n+m)\log n)$。

Code:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define lg(x)(31-__builtin_clz(x))
const int N=5e5+5;
int s[N],len,n,Len[N],pos[N],m,bel[N];
int c[N],x[N],y[N],sa[N],height[N];
int __=233;
char t[N];
int st[20][N];
inline int lcp(int l,int r){
    if(++l>r)return len;
    const int g=lg(r-l+1);
    return min(st[g][l],st[g][r-(1<<g)+1]);
}
void sort_(){
    int m=__;
    for(int i=1;i<=len;++i)++c[x[i]=s[i]];
    for(int i=1;i<=m;++i)c[i]+=c[i-1];
    for(int i=len;i;--i)sa[c[x[i]]--]=i;
    for(int k=1;k<=len;k<<=1){
        int p=0;
        for(int i=len-k+1;i<=len;++i)y[++p]=i;
        for(int i=1;i<=len;++i)
        if(sa[i]>k)y[++p]=sa[i]-k;
        for(int i=1;i<=m;++i)c[i]=0;
        for(int i=1;i<=len;++i)++c[x[i]];
        for(int i=1;i<=m;++i)c[i]+=c[i-1];
        for(int i=len;i;--i)sa[c[x[y[i]]]--]=y[i];
        swap(x,y);
        x[sa[p=1]]=1;
        for(int i=2;i<=len;++i)
        x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p);
        if(p==len)break;
        m=p;
    }
    for(int i=1,k=0;i<=len;++i)
    if(x[i]!=1){
        k-=!!k;
        int j=sa[x[i]-1];
        while(s[i+k]==s[j+k])++k;
        height[x[i]]=k;
    }
    for(int i=1;i<=len;++i)st[0][i]=height[i];
    for(int j=1;j<20;++j)
        for(int i=1;i+(1<<j-1)<=len;++i)
            st[j][i]=min(st[j-1][i],st[j-1][i+(1<<j-1)]);
}
inline int askL(int pos,int len){
    int l=1,r=pos-1,ret=pos;
    while(l<=r){
        const int mid=l+r>>1;
        if(lcp(mid,pos)>=len)r=(ret=mid)-1;else l=mid+1;
    }
    return ret;
}
inline int askR(int pos,int len){
    int l=pos+1,r=::len,ret=pos;
    while(l<=r){
        const int mid=l+r>>1;
        if(lcp(pos,mid)>=len)l=(ret=mid)+1;else r=mid-1;
    }
    return ret;
}
struct info{
    int l,r,id,fh;
};
vector<info>vec[N];
long long ans[N];
int B[N];
inline void add(int i){for(;i<=len;i+=i&-i)++B[i];}
inline int ask(int i){int x=0;for(;i;i&=i-1)x+=B[i];return x;}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        cin>>t;
        pos[i]=len+1;
        Len[i]=strlen(t);
        for(int j=0;t[j];++j)
        s[++len]=t[j],bel[len]=i;
        if(i!=n)s[++len]=++__;
    }
    sort_();
    for(int i=1;i<=m;++i){
        int k,l,r;
        cin>>l>>r>>k;
        int L=askL(x[pos[k]],Len[k]),R=askR(x[pos[k]],Len[k]);
        vec[R].push_back((info){l,r,i,1});
        vec[L-1].push_back((info){l,r,i,-1});
    }
    for(int i=1;i<=len;++i){
        if(bel[sa[i]])add(bel[sa[i]]);
        for(auto f:vec[i])
        ans[f.id]+=f.fh*(ask(f.r)-ask(f.l-1));
    }
    for(int i=1;i<=m;++i)
    cout<<ans[i]<<'\n';
    return 0;
}