[OOI 2023] A task for substrings / 字符串问题 题解

[OOI 2023] A task for substrings / 字符串问题
题意:给定一个文本串和 \(n\) 个模式串,有 \(m\) 次询问,每次询问文本串中 \([l,r]\) 的模式串出现次数之和。\(n,m \le 5\times 10^5\),\(|t| \le 5\times 10^6\),\(\sum |s| \le 10^6\)。

第一想法是预处理出 \(pre_i\) 表示前 \([1,i]\) 的答案,回答询问只要 \(pre_r-pre_{l-1}\) 即可。但是这会多算一些开头在 \(l\) 前面,结尾在 \([l,r]\) 的串,我们称之为坏串。

我们记 \(id_i\) 表示文本串第 \(i\) 位可匹配的最长模式串编号。则每次询问时,我们找到一个最大的位置 \(p\),使得 \(p-|s_{id_p}|+1 \le l\),那么在 \(p\) 之后的位置就没有坏串,这可以线段树二分解决。

用 \(pre_r-pre_p\) 可以剔除所有坏串,却也会漏掉被包含在 \([l,p]\) 内的好串。只要加上这些好串答案就对了。发现这些好串都是包含在那个最长坏串 \(id_p\) 里的。我们记 \(suf_{i,j}\) 表示第 \(i\) 个模式串的长为 \(j\) 的后缀中模式串的出现次数之和。这可以建一个反串 AC 自动机当成前缀求出来。

答案就为 \(pre_r-pre_p+suf_{id_{p},p-l+1}\)。时间复杂度 \(O(\sum|s|+|t|\log |t|+m\log |t|)\)。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m,len;
const int N = 5e6+10;
string t,s[500005];
ll id[N],pre[N];
vector<ll>suf[500005];
struct trie{
    int tr[N][26],fail[N],num[N],tot;
    ll val[N];
    void ins(string s,int id){
        int p=0;
        for(int i=0;i<s.size();i++){
            int c=s[i]-'a';
            if(!tr[p][c])tr[p][c]=++tot;
            p=tr[p][c];
        }
        val[p]=1;
        num[p]=id;
    }
    void build(){
        queue<int>q;
        for(int i=0;i<26;i++){
            if(tr[0][i]){
                q.push(tr[0][i]);
            }
        }
        while(!q.empty()){
            int u=q.front();q.pop();
            if(!num[u])num[u]=num[fail[u]];
            for(int i=0;i<26;i++){
                if(tr[u][i]){
                    fail[tr[u][i]]=tr[fail[u]][i];
                    q.push(tr[u][i]);
                    val[tr[u][i]]+=val[fail[tr[u][i]]];
                }else{
                    tr[u][i]=tr[fail[u]][i];
                }
            }
        }
    }
    void get(string s,int i,bool typ){
        suf[i].resize((int)s.size()+1,0);
        int p=0,j=1;
        for(auto c:s){
            p=tr[p][c-'a'];
            suf[i][j]=suf[i][j-1]+val[p];
            if(typ){
                pre[j]=pre[j-1]+val[p];
                id[j]=num[p];
            }
            j++;
        }
    }
}ac1,ac2;
struct sgt{
    #define ls p<<1
    #define rs p<<1|1
    int mi[N<<2];
    void pushup(int p){
        mi[p]=min(mi[ls],mi[rs]);
    }
    void build(int p,int l,int r){
        if(l==r){
            mi[p]=l-s[id[l]].size()+1;
            return;
        }
        int mid=(l+r)/2;
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(p);
    }
    int findp(int p,int l,int r,int ql,int qr){
        if(l>qr||r<ql||mi[p]>=ql)return -1;
        if(l==r)return l;
        int mid=(l+r)/2;
        int pos=findp(rs,mid+1,r,ql,qr);
        if(pos!=-1)return pos;
        return findp(ls,l,mid,ql,qr); 
    }
}T;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    cin>>t;
    len=t.size();
    for(int i=1;i<=n;i++){
        cin>>s[i];
        ac1.ins(s[i],i);
        reverse(s[i].begin(),s[i].end());
        ac2.ins(s[i],i);
    }
    ac1.build();
    ac2.build();
    ac1.get(t,0,1);
    T.build(1,1,len);
    for(int i=1;i<=n;i++){
        ac2.get(s[i],i,0);
    }
    for(int i=1;i<=m;i++){
        int l,r;
        cin>>l>>r;
        int p=T.findp(1,1,len,l,r);
        if(p==-1){
            cout<<pre[r]-pre[l-1]<<" ";
        }else{
            cout<<pre[r]-pre[p]+suf[id[p]][p-l+1]<<" ";
        }
    }
    return 0;
}
posted @ 2026-09-13 17:00  farenara_sayowell  阅读(9)  评论(0)    收藏  举报