[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;
}

浙公网安备 33010602011771号