【洛谷P6257】First of Her Name

题目

题目链接:https://www.luogu.com.cn/problem/P6257
众所周知,皇室家族的名字非常有讲究。而作为研究皇室的历史学家的你,最近接到了一个艰巨的任务——分析王国历史中所有皇室夫人的名字。

王国历史上有 \(n\) 位皇室夫人,方便起见,我们将其从 \(1\)\(n\) 编号。除了 \(1\) 号夫人外,其余夫人的名字均为一个大写字母连接着她母亲的名字。而 \(1\) 号夫人作为王国的首任王后,她的名字只有一个大写字母。

例如,由于 AENERYSAENERYS 组成,因此 ENERYSAENERYS 的母亲。相似地,AENERYSDAENERYSYAENERYS 的母亲。

你知道王国历史上所有皇室夫人的姓名与关系,而你需要完成的任务是,对于其他历史学家感兴趣的名字串 \(s\),总共有多少位夫人的名字是以 \(s\) 起始的。

例如在样例的皇室族谱中,SAENERYS 的这一支(包含 YSRYSERYSNERYSENERYS 这几位夫人)均只有一位女儿。接下来 AENERYS 有两位女儿,分别是 DAENERYS,以及女儿是 RYAENERYSYAENERYS

在这个皇室家族内,有两位夫人的名字以 RY 起始,她们是 RYSRYAENERYS。而 ERYSENERYS 均以 E 起始。名字以 N 起始的仅有一位夫人 NERYS。同样地,以 S 起始的仅有首位王后 S。而没有任何一位夫人的名字以 AY 起始。

思路

将每一个询问串反过来,问题转化为求每一个询问串是多少个人名字的后缀。
将所有询问串扔进一个 AC 自动机内,建出 fail 树,然后按照人名的 Trie 找到每一个人的名字在 AC 自动机上能匹配到的最长的串对应节点,将这一个节点的 tag 加一。显然这个人能贡献的询问串是打 tag 的点 fail 链上的所有询问。
然后在 fail 树上做前缀和即可。为了保证计算到一个点时这个点子树内的贡献全部已经加上,我们可以按深度来枚举点。
时间复杂度 \(O(n+m+\sum |S|)\)

代码

#include <bits/stdc++.h>
#define mp make_pair
using namespace std;

const int N=1000010;
int n,m,tot,head[N],ans[N];
char c[N],s[N];

struct edge
{
	int next,to;
}e[N];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

struct ACA
{
	int tot,ch[N][26],fail[N],cnt[N];
	vector<int> id[N],dep[N];
	
	void ins(char *s,int x)
	{
		int p=0,len=strlen(s+1);
		for (int i=len;i>=1;i--)
		{
			if (!ch[p][s[i]-'A'])
			{
				ch[p][s[i]-'A']=++tot;
				dep[len-i+2].push_back(tot);
			}
			p=ch[p][s[i]-'A'];
		}
		id[p].push_back(x);
	}
	
	void build()
	{
		queue<int> q;
		for (int i=0;i<26;i++)
			if (ch[0][i]) q.push(ch[0][i]);
		while (q.size())
		{
			int u=q.front(); q.pop();
			for (int i=0;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];
		}
	}
	
	void query(int x)
	{
		for (int i=1000001;i>=1;i--)
			for (int j=0;j<dep[i].size();j++)
			{
				int p=dep[i][j];
				for (int k=0;k<id[p].size();k++)
					ans[id[p][k]]=cnt[p];
				cnt[fail[p]]+=cnt[p];
			}	
	}
}AC;

void dfs(int x,int p)
{
	AC.cnt[p]++;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		dfs(v,AC.ch[p][c[v]-'A']);
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1,x;i<=n;i++)
	{
		while (c[i]=getchar())
			if (c[i]>='A' && c[i]<='Z') break;
		scanf("%d",&x);
		if (x) add(x,i);
	}
	for (int i=1;i<=m;i++)
	{
		scanf("%s",s+1);
		AC.ins(s,i);
	}
	AC.build();
	dfs(1,AC.ch[0][c[1]-'A']);
	AC.query(0);
	for (int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}
posted @ 2020-12-03 17:20  stoorz  阅读(143)  评论(0编辑  收藏  举报