Pty的字符串

题意

给一颗根节点为 1 1 1 的树(节点数 n ≤ 6 × 1 0 5 n \leq 6 \times 10^5 n6×105),点与点之间用一个小写字母连接。
给一个字符串 S S S (长度 l e n ≤ 8 × 1 0 6 len \leq 8 \times 10^6 len8×106 ),称 S [ l … r ] S[l \dots r] S[lr] 与树上的一条从上往下的路径匹配当且仅当 S [ l … r ] S[l \dots r] S[lr] 等于树上该条路径所形成的字符串,求所有的匹配对数。

题解

显然把该树上的广义 s a m sam sam 建出来。

然后把 S S S 一个一个字符放进去匹配。对于 S [ 1 … i ] S[1 \dots i] S[1i],求出其在树上的最长匹配长度 l e le le 和匹配节点 k k k,所有匹配长度乘以其出现次数的和就是答案。

具体地,我们维护 f [ x ] f[x] f[x] 表示 s a m sam sam 上该节点所代表的字符串的出现次数。显然,对于一个长度,乘上它的出现次数就是总数。所以我们还要求出它在后缀树上的祖先的长度乘上出现次数,因为其祖先长度小于 l e le le ,所以最长匹配长度就是 l e n [ x ] − l e n [ f a [ x ] ] len[x]-len[fa[x]] len[x]len[fa[x]]
这玩意我们用树上前缀和维护一下即可:令 g [ x ] = ∑ ( l e n [ y ] − l e n [ f a [ y ] ] ) × f [ y ] g[x] = \sum (len[y]-len[fa[y]]) \times f[y] g[x]=(len[y]len[fa[y]])×f[y] y y y x x x 在后缀树上的祖先)。
然后对于一组 ( k , l e ) (k,le) (k,le) 其答案为 ( l e − l e n [ f a [ k ] ] ) × f [ k ] + g [ f a [ k ] ] (le-len[fa[k]]) \times f[k]+g[fa[k]] (lelen[fa[k]])×f[k]+g[fa[k]]

把所有 ( k , l e ) (k,le) (k,le) 的答案加起来即可。

题解

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e6+10;
const int M=2e7+10;
int n;
struct Trie{
	int t[N][3],now,las,pos[N],len[N],fa[N],mx[N],f[N];
	vector<int> ch[N][3];
	LL s[N];
	queue<int> q;
	int tot,ver[N],fst[N],nxt[N];
	inline void jia(int c,int id){
		int k=las;las=++now;len[now]=len[k]+1;pos[id]=las;f[las]=1;
		while(k&&!t[k][c])t[k][c]=now,k=fa[k];
		if(!k)fa[now]=1;
		else{
			int q=t[k][c];
			if(len[q]==len[k]+1)fa[now]=q;
			else{
				fa[++now]=fa[q];fa[q]=fa[las]=now;len[now]=len[k]+1;
				for(int j=0;j<3;++j)t[now][j]=t[q][j];
				while(k&&t[k][c]==q)t[k][c]=now,k=fa[k];
			}
		}
	}
	inline void add(int x,int y){ver[++tot]=y;nxt[tot]=fst[x];fst[x]=tot;}
	inline void jian(){for(int i=2;i<=now;++i)add(fa[i],i);}
	void dfs1(int x){
		for(int i=fst[x];i;i=nxt[i]){
			int y=ver[i];
			dfs1(y);
			f[x]+=f[y];
		}
	}
	void dfs2(int x){
		s[x]+=1ll*(len[x]-len[fa[x]])*f[x];
		for(int i=fst[x];i;i=nxt[i]){
			int y=ver[i];
			s[y]+=s[x];
			dfs2(y);
		}
	}
	inline void build(){
		las=now=pos[1]=1;
		q.push(1);
		while(q.size()){
			int x=q.front();q.pop();
			for(int i=0;i<3;++i){
				if(ch[x][i].size()){
					for(int j=0;j<(int)ch[x][i].size();++j){
						int k=ch[x][i][j];
					//	cout<<"Trie "<<x<<" "<<i<<" "<<ch[x][i]<<endl;
						las=pos[x];
						jia(i,k);
						q.push(k);
					}
				}
			}
		}
	}
}T;
char g[M];
LL ans;
int main(){
	scanf("%d",&n);
	for(int i=2;i<=n;++i){
		int x;char s[2];
		scanf("%d",&x);scanf("%s",s);
		T.ch[x][s[0]-'a'].push_back(i);
	}
	T.build();T.jian();T.dfs1(1);T.dfs2(1);
	scanf("%s",g);
	n=strlen(g);
	int k=1,len=0;
	for(int i=0;i<n;++i){
		int c=g[i]-'a';
		while(k!=1&&!T.t[k][c])k=T.fa[k],len=T.len[k];
		if(!T.t[k][c])k=1,len=0;
		else k=T.t[k][c],len++;
	//	cout<<"FAQ "<<k<<" "<<len<<" "<<T.f[k]<<endl;
		ans=ans+1ll*(len-T.len[T.fa[k]])*T.f[k]+T.s[T.fa[k]];
	}
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值