CF666E Forensic Examination(后缀自动机,可持久化线段树合并)
给你一个串\(S\),以及一个字符串数组\(T_{1,2,...m}\),\(q\)次询问,每次问\(S\)的子串\(S[p_l,...p_r]\)在\(T_{l...r}\)中的哪个串的出现次数最多,并输出出现次数。
做法:
对串\(S\)和数组\(T\)建立后缀自动机。
在后缀自动机上找到\(S[l,r]\)这个子串对应的节点u,这是一个经典操作。
数组\(T\)内每个字符串都可以视作一种颜色。
现在问题转变为,每次询问一个节点,节点的link树子树内部哪种颜色出现次数最多,这个次数是多少。
这里可以线段树合并,或者树上启发式合并来做。
这里选择用线段树合并的做法。
时间复杂度\(O(nlogn)\)。
#include<bits/stdc++.h>
using namespace std;
const int maxn=6e5+10;
int m,q;
int n,len[maxn<<1],link[maxn<<1],nxt[maxn<<1][26],tot=1,lst=1;
string s;
string t[maxn];
void sam_extend (char c) {
int cur=++tot,p=lst;
len[cur]=len[lst]+1;
while (p&&!nxt[p][c-'a']) {
nxt[p][c-'a']=cur;
p=link[p];
}
if (!p) link[cur]=1;
else {
int q=nxt[p][c-'a'];
if (len[p]+1==len[q]) {
link[cur]=q;
}
else {
int clone=++tot;
len[clone]=len[p]+1;
for (int i=0;i<26;i++) {
nxt[clone][i]=nxt[q][i];
}
link[clone]=link[q];
while (p&&nxt[p][c-'a']==q) {
nxt[p][c-'a']=clone;
p=link[p];
}
link[q]=link[cur]=clone;
}
}
lst=cur;
}
const int M=maxn*40;
int c[M],id[M],lson[M],rson[M],tol,T[maxn<<1];
int up (int u,int l,int r,int p,int v) {
if (!u) u=++tol;
if (l==r) {
c[u]+=v;
id[u]=l;
return u;
}
int mid=(l+r)>>1;
if (p<=mid) lson[u]=up(lson[u],l,mid,p,v);
if (p>mid) rson[u]=up(rson[u],mid+1,r,p,v);
c[u]=max(c[lson[u]],c[rson[u]]);
if (c[rson[u]]==c[u]) id[u]=id[rson[u]];
if (c[lson[u]]==c[u]) id[u]=id[lson[u]];
return u;
}
int merge (int x,int y,int l,int r) {
if (!x||!y) return x+y;
int u=++tol;
if (l==r) {
c[u]=c[x]+c[y];
id[u]=l;
return u;
}
int mid=(l+r)>>1;
lson[u]=merge(lson[x],lson[y],l,mid);
rson[u]=merge(rson[x],rson[y],mid+1,r);
c[u]=max(c[lson[u]],c[rson[u]]);
if (c[rson[u]]==c[u]) id[u]=id[rson[u]];
if (c[lson[u]]==c[u]) id[u]=id[lson[u]];
return u;
}
pair<int,int> query (int u,int l,int r,int L,int R) {
//printf("%d %d %d %d %d\n",u,l,r,L,R);
if (L>R) return make_pair(0,0);
if (l>=L&&r<=R) {
return make_pair(c[u],id[u]);
}
int mid=(l+r)>>1;
pair<int,int> ans=make_pair(0,0);
if (L<=mid) ans=query(lson[u],l,mid,L,R);
if (R>mid) {
pair<int,int> tt=query(rson[u],mid+1,r,L,R);
if (tt.first>ans.first) {
ans.first=tt.first;
ans.second=tt.second;
}
}
return ans;
}
vector<int> g[maxn<<1];
int father[25][maxn<<1];
void dfs (int u) {
for (int i=1;i<=20;i++) father[i][u]=father[i-1][father[i-1][u]];
for (int v:g[u]) {
father[0][v]=u;
dfs(v);
T[u]=merge(T[u],T[v],1,m);
}
}
int ed[maxn];
int main () {
ios::sync_with_stdio(false);
cin>>s;
for (int i=0;i<s.size();i++) {
sam_extend(s[i]);
ed[i+1]=lst;
}
cin>>m;
for (int i=1;i<=m;i++) {
cin>>t[i];
lst=1;
for (char ch:t[i]) sam_extend(ch),T[lst]=up(T[lst],1,m,i,1);
}
for (int i=2;i<=tot;i++) g[link[i]].push_back(i);
dfs(1);
cin>>q;
while (q--) {
int A,B,C,D;
cin>>C>>D>>A>>B;
int u=ed[B];
for (int i=20;i>=0;i--) {
if (father[i][u]&&len[father[i][u]]>=B-A+1) {
u=father[i][u];
}
}
pair<int,int> ans=query(T[u],1,m,C,D);
if (ans.first==0) {
ans={0,C};
}
cout<<ans.second<<" "<<ans.first<<'\n';
}
}