codeforces #305 E Mike and friends

原问题可以转化为:给定第k个字符串,求它在L-R的字符串里作为子串出现了多少次

定义子串为字符串的某个前缀的某个后缀(废话)

等价于我们把一个字符串插入到trie里,其过程中每个经过的节点和其向上的fail链上的点都是该字符串的子串

又因为对于一条fail链,u向上能访问到v当前仅当u在v的子树内

那么原问题又变成了:

将L-R个字符串按照上述方法插入到trie中并将经过的节点的val值增加

求第k个字符串对应的单词节点在fail树上的子树的权值和

又因为查询的信息满足区间可减性,所以我们可以建出fail树

对fail树用可持久化线段树维护DFS序 完成单点修改和子树查询

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

const int maxn=500010;
int n,m,sum=0;
int L,R,k;
int pos[maxn];
int rt[maxn];
char s[maxn];
queue<int>Q;

int h[maxn],cnt=0;
int A[maxn],B[maxn],tot=0;
struct edge{
	int to,next;
}G[maxn<<1];

void add(int x,int y){++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt;}
void Get_DFS(int u){
	A[u]=++tot;
	for(int i=h[u];i;i=G[i].next)Get_DFS(G[i].to);
	B[u]=tot;
}

struct Seg_Tree{
	int L,R,v;
}t[11000010];

void build(int &o,int L,int R){
	o=++sum;
	if(L==R)return;
	int mid=(L+R)>>1;
	build(t[o].L,L,mid);
	build(t[o].R,mid+1,R);
}
void modify(int &o,int L,int R,int p){
	t[++sum]=t[o];o=sum;
	if(L==R){t[o].v++;return;}
	int mid=(L+R)>>1;
	if(p<=mid)modify(t[o].L,L,mid,p);
	else modify(t[o].R,mid+1,R,p);
	t[o].v=t[t[o].L].v+t[t[o].R].v;
}
int ask(int o,int L,int R,int x,int y){
	if(L>=x&&R<=y)return t[o].v;
	int mid=(L+R)>>1;
	if(y<=mid)return ask(t[o].L,L,mid,x,y);
	else if(x>mid)return ask(t[o].R,mid+1,R,x,y);
	else return ask(t[o].L,L,mid,x,y)+ask(t[o].R,mid+1,R,x,y);
}

struct Trie{
	int cnt;
	int t[maxn][26];
	int fail[maxn],fa[maxn];
	void init(){
		cnt=1;fail[0]=1;
		for(int i=0;i<26;++i)t[0][i]=1;
	}
	int insert(){
		int len=strlen(s+1);
		int now=1;
		for(int i=1;i<=len;++i){
			int id=s[i]-'a';
			if(!t[now][id])t[now][id]=++cnt,fa[t[now][id]]=now;
			now=t[now][id];
		}return now;
	}
	void build_fail(){
		Q.push(1);fail[1]=0;
		while(!Q.empty()){
			int u=Q.front();Q.pop();
			for(int i=0;i<26;++i){
				if(t[u][i]){
					int k=fail[u];
					while(!t[k][i])k=fail[k];
					fail[t[u][i]]=t[k][i];
					add(t[k][i],t[u][i]);
					Q.push(t[u][i]);
				}
			}
		}return;
	}
	void UPD(){
		build(rt[0],1,cnt);
		for(int i=1;i<=n;++i){
			rt[i]=rt[i-1];
			for(int j=pos[i];j!=1;j=fa[j]){
				modify(rt[i],1,cnt,A[j]);
			}
		}return;
	}
}AC;

int main(){
	scanf("%d%d",&n,&m);
	AC.init();
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		pos[i]=AC.insert();
	}AC.build_fail();Get_DFS(1);AC.UPD();
	for(int i=1;i<=m;++i){
		scanf("%d%d%d",&L,&R,&k);
		printf("%d
",ask(rt[R],1,AC.cnt,A[pos[k]],B[pos[k]])-ask(rt[L-1],1,AC.cnt,A[pos[k]],B[pos[k]]));
	}return 0;
}

  

即可

原文地址:https://www.cnblogs.com/joyouth/p/5352844.html