JZOJ7044. 2021.04.05【2021省赛模拟】悄悄话(word)题解

本文详细介绍了如何使用AC自动机和Fail树解决字符串处理问题,包括构建过程、单点查询优化及线段树的维护,最终实现O(n log n)的时间复杂度解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其实是一道挺简单的题目
但是自己之前没怎么用过failfailfail树所以不会还是菜

对所有的串建出ACACAC自动机,建出failfailfail
failfailfail树就是由fail[i]−>ifail[i]->ifail[i]>i的边组成的树,与ACACAC自动机上节点一一对应
然后一个简单O(n∑len[i])DPO(n\sum len[i])DPO(nlen[i])DP,设f[i]f[i]f[i]为选择iii最后一个串时的最大答案
那么f[i]=max(f[j])f[i]=max(f[j])f[i]=max(f[j]) (j(j(jiii的子串)))
建出failfailfail树后,对于每个字符串的前缀单点查询,最后加上自己的权值后修改这个串子树的maxmaxmax值,用线段树即可维护
就可以在O((∑len[i]) log∑len[i])O((\sum len[i])\,log\sum len[i])O((len[i])loglen[i])时间内完成

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 1000005
#define ll long long
#include <vector>
using namespace std;
int trie[N][26],dfn[N],fail[N];
int f[N][3],q[N],size[N],cun[N],ys[N],len[N];
struct node{
	ll max,flag;
}tree[N<<1];
ll dp[N],val[N],s;
int i,j,k,m,n,o,p,l,t,tot,now,sum;
char c[N]; 
vector<int> a[N];
queue<int> h;
void build(int n)
{
	int now=0;
	for (int i=1;i<=n;i++)
	{
		if (!trie[now][c[i]-'a'])
			trie[now][c[i]-'a']=++sum;
		now=trie[now][c[i]-'a'],cun[++cun[0]]=now;
	}
}
void insert(int x,int y) {f[++t][1]=y,f[t][2]=q[x],q[x]=t;}
void work()
{
	int now=0;
	for (i=0;i<=25;i++)
		if (trie[now][i]) h.push(trie[now][i]);
	while (!h.empty())
	{
		int st=h.front();h.pop();
		for (int i=0;i<=25;i++)
			if (trie[st][i]) fail[trie[st][i]]=trie[fail[st]][i],h.push(trie[st][i]);
			else trie[st][i]=trie[fail[st]][i];
	}
	for (i=1;i<=sum;i++) insert(fail[i]+1,i+1);//printf("%d %d\n",fail[i],i);
}
void dg(int t)
{
	dfn[t]=++tot,size[t]=1;
	for (int k=q[t];k;k=f[k][2])
	{
		dg(f[k][1]);
		size[t]+=size[f[k][1]];
	}
}
ll max(ll a,ll b) {return a>b?a:b;}
void downdata(ll x)
{
	if (!tree[x].flag) return;
	ll k=tree[x].flag;
	tree[x<<1].max=max(tree[x<<1].max,k),tree[x<<1|1].max=max(tree[x<<1|1].max,k);
	tree[x<<1].flag=max(tree[x<<1].flag,k),tree[x<<1|1].flag=max(tree[x<<1|1].flag,k);
	tree[x].flag=0;
}
void change(ll x,ll l,ll r,ll st,ll en,ll ad)
{
	if (l>=st&&r<=en) tree[x].max=max(tree[x].max,ad),tree[x].flag=max(tree[x].flag,ad);
	else {
		downdata(x);
		ll mid=(l+r)/2;
		if (en<=mid) change(x<<1,l,mid,st,en,ad);
		else if (st>mid) change(x<<1|1,mid+1,r,st,en,ad);
		else change(x<<1,l,mid,st,mid,ad),change(x<<1|1,mid+1,r,mid+1,en,ad);
		tree[x].max=max(tree[x<<1].max,tree[x<<1|1].max);
	}
}
ll query(ll x,ll l,ll r,ll pos)
{
	if (l==r) return tree[x].max;
	else {
		downdata(x);
		ll mid=(l+r)/2;
		if (pos<=mid) return query(x<<1,l,mid,pos);
		else return query(x<<1|1,mid+1,r,pos);
	}
}
int main()
{
	freopen("word.in","r",stdin);
	freopen("word.out","w",stdout);
	scanf("%d\n",&n);
	for (i=1;i<=n;i++)
	{
		scanf("%s %lld\n",c+1,&val[i]);
		cun[0]=0;len[i]=strlen(c+1);
		build(len[i]);
		for (j=1;j<=cun[0];j++) a[i].push_back(cun[j]);
	} 
	work();
	dg(1);
	ll ans=0;
	for (i=1;i<=n;i++)
	{
		dp[i]=0;
		for (j=0;j<=len[i]-1;j++)
			dp[i]=max(dp[i],query(1,1,tot,dfn[a[i][j]+1]));
		dp[i]+=val[i];
		change(1,1,tot,dfn[a[i][len[i]-1]+1],dfn[a[i][len[i]-1]+1]+size[a[i][len[i]-1]+1]-1,dp[i]);
	}      
	for (i=1;i<=n;i++) ans=(dp[i]>ans?dp[i]:ans);
	printf("%lld\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值