Death DBMS题解(AC自动机)

题目传送门 CF1437G

好题

观察这道题,发现有关字串的题目,一般来说,这种题都要构建\(AC\)自动机,所以考虑构建。

构建之后,原来的所有 \(fail\) 是一个树形结构。

解法 \(1\):

考虑从询问入手,那么对于每一个询问,等价于就是查询每一个 \(Q_i\) 包含的后缀的最大值,再对这些最大值取 \(max\),那么 \(Q\) 在跳 \(fail\) 树时,查询所有 \(Q_i\) 点到根的最大值,再将这些最大值取 \(max\),就是答案,考虑:树剖 \(+\) 单点修改。

时间复杂度 \(O(n~log^2~n)\)

解法 \(2\)

考虑从修改入手,那么对于修改字符串 \(s\),代表字符串 \(s\) 的点的整个子树的最大值都会受到影响,修改整个子树内的每个点的最大值,查询 \(Q\) 时,让 \(Q\) 在跳 \(fail\) 树时对于每一个 \(Q_i\) 的值取一个 \(max\),那么考虑:子树修改 \(+\) 单点查询

注意:用 \(multiset\)(可重并查集,且自动排序) 去维护

时间复杂度 \(O(n~log^2~n)\),因为有些东西并不是 \(O(1)\)

解法 \(3\)

Important

我们发现:对于 \(i\) 这个节点,他这个点的值会且只会影响以他为根的这棵子树,于是我们可以考虑用 \(dfs\),在进入这个点的时候加入有关这个点的一些元素,退出这个点的时候再删除。

在线是没前途的,优秀的时间复杂度一般都是离线的,考虑将问题离线下来,然后用线段树分治,表示该时间段的最大值,每新进入一个点的时候就更新一下这个线段树,这里可以用标记永久化

然后我们发现,最大值不好删除,这里又是一个 \(Trick\)把删除改成撤销,每次修改时记录修改位置和原来的最大值,退出这个点时把这些位置改回来

分析一下时间复杂度:

每次修改会产生 \(\log m\) 的时间复杂度,最多有 \(n+q\) 次修改,故时间复杂度为 \(O((n+q) \log m)\)

每次修改最多改变 \(\log m\) 个节点,最多有 \(n+q\) 次修改,撤回的时候时间复杂度为 \(O((n+q) \log m)\)

撤去常数,\(n,m,q\) 是同一个级别,所以时间复杂度就是 \(O(n \log n)\) 级别!!!

分析一下空间复杂度:

最多要记录 \((n+q) \log n\) 的修改,所以空间复杂度是 \(O(n \log n)\) 级别,不过因为 \(n\le 3e5\),空间限制是 \(500MB\),所以可以过。

这里科普一下,\(128MB\) 就可以开一个形如 \(long~long~a[1e7]\) 的数组

贴上代码:

#include<bits/stdc++.h>
#define ll long long
#define PLL pair<ll,ll>
using namespace std;
const ll N=3e5+50,zero=0;
ll n,m;
vector<ll> e[N],word[N],q[N];
vector<PLL> sz1[N];//i在不同时段的数值 
char s[N];
ll ch[N][26],idx;
void insert(char str[],ll id)
{
	ll p=0,len=strlen(str);
	for(ll i=0;i<len;i++)
	{
		ll shu=str[i]-'a';
		if(!ch[p][shu]) ch[p][shu]=++idx;
		p=ch[p][shu];
	}
	word[p].push_back(id);
}
ll fail[N];
void get_fail()
{
	queue<ll> q;
	for(ll i=0;i<26;i++)
		if(ch[0][i]) e[0].push_back(ch[0][i]),q.push(ch[0][i]);
	while(!q.empty())
	{
		ll p=q.front();
		q.pop();
		for(ll i=0;i<26;i++)
		{
			if(ch[p][i]) fail[ch[p][i]]=ch[fail[p]][i],e[ch[fail[p]][i]].push_back(ch[p][i]),q.push(ch[p][i]);
			else ch[p][i]=ch[fail[p]][i];
		}
	}
}
ll ans[N],tr[N*4];
void gai(ll wz,ll l,ll r,ll le,ll ri,ll x,vector<PLL> &op)
{
	if(le<=l&&ri>=r)
	{
		op.push_back(make_pair(wz,tr[wz]));
		tr[wz]=max(tr[wz],x);
		return ;
	}
	ll mid=(l+r)/2;
	if(le<=mid) gai(wz*2,l,mid,le,ri,x,op);
	if(ri>mid) gai(wz*2+1,mid+1,r,le,ri,x,op);
}
ll query(ll md)
{
	ll l=1,r=m,mid,p=1,re=tr[p];
	while(l<r)
	{
		mid=(l+r)/2;
		if(md<=mid) r=mid,p=p*2;
		else l=mid+1,p=p*2+1;
		re=max(re,tr[p]);
	}
	return re;
}
void dfs(ll wz)
{
	vector<PLL> tzy;
	ll cd=word[wz].size();
	for(ll i=0;i<cd;i++)
	{
		ll now=word[wz][i],cd1=sz1[now].size();
		sz1[now].push_back(make_pair(m+1,zero));
		if(sz1[now][0].first!=1) gai(1,1,m,1,sz1[now][0].first-1,0,tzy);
		for(ll j=0;j<cd1;j++) gai(1,1,m,sz1[now][j].first,sz1[now][j+1].first-1,sz1[now][j].second,tzy);
	}
	cd=q[wz].size();
	for(ll i=0;i<cd;i++)
	{
		ll now=q[wz][i];
		ans[now]=max(ans[now],query(now));
	}
	cd=e[wz].size();
	for(ll i=0;i<cd;i++)
		dfs(e[wz][i]);
	cd=tzy.size();
	for(ll i=cd-1;i>=0;i--)
		tr[tzy[i].first]=tzy[i].second;
}
int main()
{
	memset(tr,-1,sizeof tr);
	scanf("%lld %lld",&n,&m);
	for(ll i=1;i<=n;i++)
	{
		scanf("%s",s);
		insert(s,i);
	}
	get_fail();
	ll op,t1,t2;
	for(ll i=1;i<=m;i++)
	{
		ans[i]=-114514;
		scanf("%lld",&op);
		if(op==1)
		{
			scanf("%lld %lld",&t1,&t2);
			sz1[t1].push_back(make_pair(i,t2));
		}
		else
		{
			scanf("%s",s);
			ll len=strlen(s),p=0;
			ans[i]=-1;
			for(ll j=0;j<len;j++)
			{
				p=ch[p][s[j]-'a'];
				q[p].push_back(i);
			}
		}
	}
	dfs(0);
	for(ll i=1;i<=m;i++)
		if(ans[i]!=-114514) printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2023-09-17 22:46  傻阙的缺  阅读(25)  评论(0)    收藏  举报