题解 CF666E【Forensic Examination】
题意
给 $m$ 个文本串 $T_{1,2,...,m}$ 和一个长度为 $n$ 的字符串 $S$,$q$ 次询问,设 $\text{app}(s,t)$ 表示 $s$ 在 $t$ 中的出现次数,求 $\max_{i=pl}^{pr}\text{app}(S[l,r],T_i)$。
$1\le n,q\le 5\times 10^5,1\le m,\sum|T|\le 5\times 10^4$。
思路
$\text{Update }2022.4.26$:增加了部分说明。
注:本题解中的 $l,r$ 和 $pl,pr$的意义与题目中相反。
考虑对于 $m$ 个串建出广义后缀自动机,对于每个节点开一个动态开点权值线段树,下标为所属串的编号,记录区间最大值和下标。对于每个前缀结点,将其所属的字符串的位置 $+1$。
然后建立 $\text{parent}$ 树,对其跑线段树合并,即可得出每个结点对应的在每个串中的出现次数。
预处理出 $S$ 的每个前缀 $S[1,i]$ 在广义 $\text{SAM}$ 中能匹配上的最长后缀的长度以及它匹配到的位置,记为 $ansi_i$ 和 $pos_i$。具体来说,若到下一个字符可转移则转移,令 $ansi_{i+1}=ansi_i+1$,否则跳 $\text{link}$ 直到有转移或者到根。
对于一次查询,先判掉 $r-l+1>ansi_r$ 的无解情况,再考虑从 $cur=pos_r$ 的位置得到 $S[l,r]$ 对应的结点。因为 $\text{parent}$ 树上的祖先都是它的后缀,所以我们在 $\text{parent}$ 树上倍增到 $len_{cur}\ge r-l+1$,此时节点 $cur$ 便包含串 $S[l,r]$,直接在 $cur$ 的线段树上查询 $[pl,pr]$ 的极值即可。
注意线段树合并时需要采取新建节点的方法而不是直接更改,因为合并完之后需要查询任意节点的答案。
这里的倍增是个很经典的技巧。
时间复杂度 $O(\sum |T|\log|T|+q\log n)$。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=1e6+10;
struct node{
int val,cnt;
inline friend bool operator<(const node &a,const node &b){
return a.cnt==b.cnt?a.val>b.val:a.cnt<b.cnt;
}
inline friend node operator+(const node &a, const node &b){
return (node){a.val,a.cnt+b.cnt};
}
node(int x=0,int y=0){val=x,cnt=y;}
};
struct Edge{
int to,next;
};
int n,m,q;
Edge a[N<<1];
int head[N],dep[N],f[20][N],cnt;
int pos[N],ansi[N];
char str[N];
inline void add(int u,int v){
cnt++;
a[cnt].to=v;
a[cnt].next=head[u];
head[u]=cnt;
}
struct Segument_Tree{
int son[2][N*20],root[N],cnt;
node val[N*20];
#define ls (son[0][rt])
#define rs (son[1][rt])
inline void insert(int &rt,int l,int r,int p){
if(!rt){
rt=++cnt;
}
if(l==r){
val[rt].cnt++;
val[rt].val=l;
return ;
}
int mid=l+r>>1;
if(p<=mid){
insert(ls,l,mid,p);
}else{
insert(rs,mid+1,r,p);
}
val[rt]=max(val[ls],val[rs]);
}
inline int merge(int a,int b,int l,int r){
if(!a||!b) return a+b;
int rt=++cnt;
if(l==r){
val[rt]=val[a]+val[b];
return rt;
}
int mid=l+r>>1;
ls=merge(son[0][a],son[0][b],l,mid);
rs=merge(son[1][a],son[1][b],mid+1,r);
val[rt]=max(val[ls],val[rs]);
return rt;
}
inline node query(int rt,int l,int r,int L,int R){
if(!rt) return node();
if(L<=l&&r<=R) return val[rt];
node ans;
int mid=l+r>>1;
if(L<=mid) ans=max(ans,query(ls,l,mid,L,R));
if(R>mid) ans=max(ans,query(rs,mid+1,r,L,R));
return ans;
}
inline void dfs(int rt){
for(int i=head[rt];i;i=a[i].next){
if(a[i].to!=f[0][rt]){
f[0][a[i].to]=rt;
dfs(a[i].to);
root[rt]=merge(root[rt],root[a[i].to],1,m);
}
}
}
}t;
struct SAM{
struct statu{
int len,link;
int ch[26];
}a[N];
int siz,last;
SAM(){
a[0].len=0;
a[0].link=-1;
last=0;
}
inline void insert(int c,int x){
int cur=++siz;
a[cur].len=a[last].len+1;
t.insert(t.root[cur],1,m,x);
int p=last;
while(p!=-1&&!a[p].ch[c]){
a[p].ch[c]=cur;
p=a[p].link;
}
if(~p){
int q=a[p].ch[c];
if(a[p].len+1==a[q].len){
a[cur].link=q;
}else{
int clone=++siz;
a[clone]=a[q];
a[clone].len=a[p].len+1;
while(p!=-1&&a[p].ch[c]==q){
a[p].ch[c]=clone;
p=a[p].link;
}
a[q].link=a[cur].link=clone;
}
}else{
a[cur].link=0;
}
last=cur;
}
inline int insert(){
static char str[N];
int T=read();
m=T;
for(int k=1;k<=T;k++){
readstr(str);
int len=strlen(str+1);
last=0;
for(int i=1;i<=len;i++){
insert(str[i]-'a',k);
}
}
return T;
}
inline void build(){
for(int i=1;i<=siz;i++){
add(a[i].link,i);
}
}
inline void calc(){
int anss=0;
int p=1;
for(int i=1;i<=n;i++){
int c=str[i]-'a';
if(a[p].ch[c]){
anss++;
p=a[p].ch[c];
}else{
while(p!=-1&&!a[p].ch[c]){
p=a[p].link;
}
if(~p){
anss=a[p].len+1;
p=a[p].ch[c];
}else{
anss=0;
p=1;
}
}
ansi[i]=anss;
pos[i]=p;
}
}
}sam;
int main(){
n=readstr(str);
m=sam.insert();
sam.build();
t.dfs(0);
for(int i=1;i<=18;i++){
for(int j=1;j<=sam.siz;j++){
f[i][j]=f[i-1][f[i-1][j]];
}
}
sam.calc();
q=read();
while(q--){
int pl=read(),pr=read(),l=read(),r=read();
int len=r-l+1;
if(len>ansi[r]){
write(pl),putc(' '),write(0),putc('\n');
continue;
}
int cur=pos[r];
for(int i=18;~i;i--){
if(sam.a[f[i][cur]].len>=len){
cur=f[i][cur];
}
}
node ans=t.query(t.root[cur],1,m,pl,pr);
if(ans.cnt==0) ans.val=pl;
write(ans.val),putc(' '),write(ans.cnt),putc('\n');
}
flush();
return 0;
}
再见 qwq~

浙公网安备 33010602011771号