题解「CF547E Mike and Friends」
转载注明来源:https://www.cnblogs.com/syc233/p/13771152.html
AC自动机+线段树合并。
将所有字符串插入AC自动机中,建出fail树。
若 \(s\) 是 \(t\) 的子串,令 \(u\) 表示 \(s\) 的末尾字符在AC自动机上对应的结点。由于AC自动机的性质,那么一定存在一个 \(t\) 在AC自动机中的结点,满足在fail树中它在 \(u\) 的子树中。
那么问题就转化为:询问 \(s_k\) 在AC自动机上的结束结点的子树中,有多少结点属于 \(s_{l \cdots r}\) (即插入Trie时经过的结点)。
这个问题可以用线段树合并解决。
每个结点开一个以字符串编号为下标的线段树。插入字符串 \(s_{id}\) 时,在经过的所有结点的线段树中的 \(id\) 位置加一,代表这个结点属于 \(s_{id}\) 。
将询问离线,线段树合并到一个结点时,在线段树上查询区间和即可。
代码挺好写的。
\(\text{Code}:\)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const int maxn=2e5+5,maxq=5e5+5;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
struct edge
{
int u,v,next;
edge(int u,int v,int next):u(u),v(v),next(next){}
edge(){}
}e[maxn];
int head[maxn],ecnt;
inline void add(int u,int v)
{
e[ecnt]=edge(u,v,head[u]);
head[u]=ecnt++;
}
struct Segment_Tree
{
int tot;
int sum[maxn<<5],ch[maxn<<5][2];
inline void update(int p)
{
sum[p]=sum[ch[p][0]]+sum[ch[p][1]];
}
void modify(int &p,int l,int r,int ps,int d)
{
if(!p) p=++tot;
sum[p]+=d;
if(l==r) return;
int mid=(l+r)>>1;
if(ps<=mid) modify(ch[p][0],l,mid,ps,d);
else modify(ch[p][1],mid+1,r,ps,d);
}
int query(int p,int l,int r,int L,int R)
{
if(!sum[p]||(L<=l&&r<=R)) return sum[p];
int mid=(l+r)>>1,ans=0;
if(L<=mid) ans+=query(ch[p][0],l,mid,L,R);
if(R>mid) ans+=query(ch[p][1],mid+1,r,L,R);
return ans;
}
int merge(int x,int y)
{
if(!x||!y) return x|y;
sum[x]+=sum[y];
ch[x][0]=merge(ch[x][0],ch[y][0]);
ch[x][1]=merge(ch[x][1],ch[y][1]);
return x;
}
}st;
int rt[maxn];
struct querys
{
int l,r,id;
querys(int l,int r,int id):l(l),r(r),id(id){}
querys(){}
};
int n,q,idx[maxn];
vector<querys> vec[maxn];
int ans[maxq];
int ch[maxn][30],tot;
int fail[maxn];
inline void insert(const char *s,int id)
{
int len=strlen(s+1),u=0;
for(int i=1;i<=len;++i)
{
int c=s[i]-'a'+1;
if(!ch[u][c]) ch[u][c]=++tot;
u=ch[u][c];
st.modify(rt[u],1,n,id,1);
}
idx[id]=u;
}
inline void GetFail()
{
queue<int> q;
memset(fail,-1,sizeof(fail));
for(int i=1;i<=26;++i)
if(ch[0][i]) q.push(ch[0][i]),fail[ch[0][i]]=0;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=1;i<=26;++i)
if(ch[u][i])
{
fail[ch[u][i]]=ch[fail[u]][i];
q.push(ch[u][i]);
}
else ch[u][i]=ch[fail[u]][i];
}
for(int i=1;i<=tot;++i)
if(~fail[i]) add(fail[i],i);
}
inline void dfs(int u)
{
for(int i=head[u];~i;i=e[i].next)
{
int v=e[i].v;
dfs(v);
rt[u]=st.merge(rt[u],rt[v]);
}
for(auto v:vec[u])
ans[v.id]=st.query(rt[u],1,n,v.l,v.r);
}
char s[maxn];
int main()
{
// freopen("CF547E.in","r",stdin);
read(n),read(q);
for(int i=1;i<=n;++i)
{
scanf(" %s",s+1);
insert(s,i);
}
memset(head,-1,sizeof(head));
GetFail();
for(int i=1,l,r,k;i<=q;++i)
{
read(l),read(r),read(k);
vec[idx[k]].push_back(querys(l,r,i));
}
dfs(0);
for(int i=1;i<=q;++i)
printf("%d\n",ans[i]);
return 0;
}