Loading

题解「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;
}

posted @ 2020-10-05 19:38  GoPoux  阅读(108)  评论(0编辑  收藏  举报