【Codeforces 666E】Forensic Examination

后缀数组+莫队+值域分块

题目大意:

给定字符串 $s$ 以及 $m$ 个字符串 $t_1\sim t_m$。

有 $q$ 次询问,每次询问给定 $l_1,r_1,l_2,r_2$,询问 $s$ 的子串 $s_{l_1,r_1}$ 在 $t_{l_2}\sim t_{r_2}$ 的哪个串中的出现次数最多,输出串的编号和出现次数。如果有多个串合法,输出编号较小的串。

题解:

后缀数组做法,需要莫队和分块辅助。

考虑把 $s$ 和所有 $t$ 连接成一个大串,中间用不同的分隔字符隔开,然后求它的后缀数组。预处理 height 数组的 ST 表。

对于每个询问,我们可以二分它在后缀数组上 LCP 大于等于子串长度的区间 $[l’,r’]$。这个区间内的每个开头位置,都完整地出现了一次子串。

我们令 $a_i$ 表示后缀数组上排名 $i$ 的后缀的开头位置所在的 $t$ 串的编号。

那么我们对每个询问,转化为询问 $a_{l’}\sim a_{r’}$ 中,值域范围在 $[l_2,r_2]$ 内的数中,出现次数最多的数的出现次数。

考虑莫队,我们可以维护每个数的出现次数,和每个出现次数的出现次数,这样可以求全局众数。

由于有值域限制,所以我们对值域分块,每个块维护一下,值域在这个块中的数的出现次数的出现次数,还有这个值域中的众数出现次数。查询的时候,边角暴力查,中间的直接 $O(1)$ 找到一个众数出现次数最多的块,然后在这个块内查询具体是哪个数即可。

令 $N=|s|+\sum|t|$。

时间复杂度 $O(N\log N+N\sqrt q+q\sqrt N)$。

考虑一些简单的优化。我们发现 $\sum |t|\leqslant 5\times 10^4$,而后缀数组中很大一部分都是 $s$ 的位置。所以我们对 $a$ 数组进行一些处理,把空的位置删掉,那么莫队的时候复杂度就得到了一些优化。

令 $M=\sum |t|$。

时间复杂度 $O(N\log N+M\sqrt q+q\sqrt M)$。可以在 $6$ 秒内通过。

Code:

#include<iostream>
#include<algorithm>
#include<utility>
#include<cmath>
using namespace std;
#define lg(x)(31-__builtin_clz(x))
#define bel(x)((x-1)/256+1)
const int N=8e5+5,M=5e4+5;
char s[N],t[N];
int S[N],m=127,n,len,col[N],height[N],sa[N],x[N],y[N],c[N];
int st[20][N];
int tot[M],ct[258][M],mx[258];
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 ssort(){
    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=0;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[1]]=p=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)]);
}
int siz;
pair<int,int>ans[N];
struct que{
    int l,r,L,R,id;
    inline bool operator<(const que&rhs)const{
        return(l/siz!=rhs.l/siz)?l<rhs.l:r<rhs.r;
    }
}q[N];
int a[N],k,pos[N];
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;
}
inline void inc(int x){
    const int bl=bel(x);
    --ct[bl][tot[x]];
    ++ct[bl][++tot[x]];
    if(tot[x]>mx[bl])mx[bl]=tot[x];
}
inline void dec(int x){
    const int bl=bel(x);
    --ct[bl][tot[x]];
    if(ct[bl][mx[bl]]==0)--mx[bl];
    ++ct[bl][--tot[x]];
}
inline pair<int,int>ask(int l,int r){
    const int bL=bel(l),bR=bel(r);
    if(bL==bR){
        int mx=-1,id=0;
        for(int i=l;i<=r;++i)
            if(tot[i]>mx)mx=tot[i],id=i;
        return make_pair(id,mx);
    }else{
        int mx=-1,id=0,blk=-1;
        for(int i=l;i<=bL*256;++i)
            if(tot[i]>mx)mx=tot[i],id=i;
        for(int i=bL+1;i<bR;++i)
            if(::mx[i]>mx)mx=::mx[i],blk=i;
        if(blk!=-1)
            for(int i=(blk-1)*256+1;i<=blk*256;++i)
                if(tot[i]==mx){
                    id=i;break;
                }
        for(int i=(bR-1)*256+1;i<=r;++i)
            if(tot[i]>mx)mx=tot[i],id=i;
        return make_pair(id,mx);
    }
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>(s+1);
    for(int i=1;s[i];++i)
        S[++len]=s[i];
    cin>>n;
    for(int i=1;i<=n;++i){
        S[++len]=++m;
        cin>>t;
        for(int j=0;t[j];++j)
            S[++len]=t[j],col[len]=i;
    }
    ssort();
    for(int i=1;i<=len;++i){
        if(col[sa[i]]!=0)a[++k]=col[sa[i]];
        pos[sa[i]]=k;
    }
    int Q;
    cin>>Q;
    for(int i=1;i<=Q;++i){
        int l,r,L,R;
        cin>>q[i].L>>q[i].R>>l>>r;
        const int ln=r-l+1;
        L=askL(x[l],ln),R=askR(x[l],ln);
        if(col[sa[L]]!=0)L=pos[sa[L]];else L=pos[sa[L]]+1;
        R=pos[sa[R]];
        if(L>R)q[i].l=1,q[i].r=0;else
        q[i].l=L,q[i].r=R;
        q[i].id=i;
    }
    siz=k/(sqrt(Q)+1)+1;
    sort(q+1,q+Q+1);
    for(int i=1,l=1,r=0;i<=Q;++i){
        while(r<q[i].r)inc(a[++r]);
        while(l>q[i].l)inc(a[--l]);
        while(l<q[i].l)dec(a[l++]);
        while(r>q[i].r)dec(a[r--]);
        ans[q[i].id]=ask(q[i].L,q[i].R);
    }
    for(int i=1;i<=Q;++i)
        cout<<ans[i].first<<' '<<ans[i].second<<'\n';
    return 0;
}