CF1207G Indie Album

题目链接

problem

\(n\)个字符串,对于第\(i\)个字符串通过以下两种方式中的一个给出。

  1. \(1\; c\),该字符串只含一个字符\(c\)

  2. \(2\ x\ c\),该字符串为第\(x(1\le x < i)\)个字符串末尾添加一个字符\(c\)得到。

\(m\)次询问,每次询问给出一个字符串\(s\)和位置编号\(x\),问在上述第\(x\)个字符串中,字符串\(s\)出现了几次。

solution

需要用到\(AC\)自动机,树状数组,\(dfs\)序。

首先将询问离线下来,对于所有询问的字符串建立一个\(AC\)自动机,从而求出\(fail\)树。然后利用\(fail\)树的性质:一个字符串在母串中出现的次数为将母串在AC自动机上跑一遍并将走到的位置权值+1,该字符串所对应的\(fail\)节点的子树权值和。

还有一个需要解决的问题,如果将母串在AC自动机上跑,如果暴力跑显然不行。所以我们发现他给出这\(n\) 个字符串的方式也是一棵树的形式。所以我们就可以用以下方式跑。

dfs(u,p) {//u为当前节点,p为其父亲在AC自动机上所跑到的节点
	将p移向u在AC自动机上跑到的节点
	将p所对应的的节点权值+1
	for(v是u的儿子) dfs(v,p)
	统计所有对于u这个串的查询的答案。
	将p所对应的节点权值-1
}

发现通过上面的方式,就可以保证每次查询的时候只有所查询的字符串在\(AC\)自动机上产生了贡献。

\(fail\)树上查询子树权值和,可以用\(dfs\)序+树状数组完成。

code

//@Author: wxyww
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
#include<cmath>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
const int N =  400010;
ll read() {
	ll x = 0,f = 1; char c = getchar();
	while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9') {x = x * 10 + c - '0',c = getchar();}
	return x * f;
}
char ss[N],s[N];
int fail[N],trie[N][30];
struct node {
	int v,nxt;
}e[N];
int idtot,siz[N],tree[N],fa[N],bh[N],ans[N],head[N],ejs,tot;
void add(int u,int v) {
	e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;
}
vector<pair<int,int> >que[N];
void update(int pos,int c) {
	while(pos <= idtot) {
		tree[pos] += c;
		pos += pos & -pos;
	}
}
int query(int pos) {
	int ret = 0;
	while(pos) {
		ret += tree[pos];
		pos -= pos & -pos;
	}
	return ret;
}
int add(char *t) {
	int n = strlen(t + 1),p = 0;
	for(int i = 1;i <= n;++i) {
		if(!trie[p][t[i] - 'a']) trie[p][t[i] - 'a'] = ++tot;
		p = trie[p][t[i] - 'a'];
	}
	// printf("!!!%d\n",p);
	return p;
}
queue<int>q;
vector<int>E[N];
void build() {
	for(int i = 0;i < 26;++i) if(trie[0][i]) q.push(trie[0][i]);

	while(!q.empty()) {
		int u = q.front();q.pop();
		E[fail[u]].push_back(u);
		for(int i = 0;i < 26;++i) {
			if(!trie[u][i]) trie[u][i] = trie[fail[u]][i];
			else fail[trie[u][i]] = trie[fail[u]][i],q.push(trie[u][i]);
		}
	}
}
void AC_dfs(int u) {
	int k = E[u].size();
	bh[u] = ++idtot;siz[u] = 1;
	for(int i = 0;i < k;++i) {
		int v = E[u][i];
		AC_dfs(v);
		siz[u] += siz[v];
	}
}
int getans(int p) {
	// printf("!!!%d %d\n",bh[p] + siz[p] - 1,bh[p]);
	return query(bh[p] + siz[p] - 1) - query(bh[p] - 1);
}
void dfs(int u,int p) {
	p = trie[p][s[u] - 'a'];
	// if(u == 2) printf("!!!%d\n",u);
	
	update(bh[p],1);
 	// printf("!!%d %d\n",u,s[u] - 'a');
 	// printf("!!!%d %d\n",u,p);
	for(int i = head[u];i;i = e[i].nxt) dfs(e[i].v,p);
	
	int k = que[u].size();
	for(int i = 0;i < k;++i) ans[que[u][i].second] = getans(que[u][i].first);

	update(bh[p],-1);

}
int main() {
	int n = read();
	for(int i = 1;i <= n;++i) {
		int opt = read();
		if(opt == 2) fa[i] = read();
		add(fa[i],i);
		// cin>>s[i];
		scanf("%s",&s[i]);
	}

	int m = read();
	for(int i = 1;i <= m;++i) {
		int id = read();
		scanf("%s",ss + 1);
		que[id].push_back(make_pair(add(ss),i));
	}

	build();

	// for(int i = 0;i < 26;++i) printf("!!%d\n",trie[0][i]);

	// for(int i = 1;i <= tot;++i) printf("!!%d\n",fail[i]);

	// printf("!!%d\n",trie[1][0]);

	AC_dfs(0);

	// printf("!!!%d %d\n",tot,idtot);

	for(int i = 1;i <= n;++i) if(!fa[i]) dfs(i,0);

	

	for(int i = 1;i <= m;++i) printf("%d\n",ans[i]);

	return 0;
}
posted @ 2019-08-24 07:57  wxyww  阅读(383)  评论(0编辑  收藏  举报