码迷,mamicode.com
首页 > 其他好文 > 详细

CF700E Cool Slogans

时间:2021-04-02 12:55:01      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:区间   %s   href   lan   query   集合   false   com   ace   

XVI.CF700E Cool Slogans

这题有SA和SAM两种做法,但事实证明,本题的SAM做法无论在思维难度还是在代码难度上,都爆踩SA做法。

首先,SA做法可以参见本人的题解

然后,SAM做法见下。

首先,我们一定可以将每个串砍掉一部分,使得我们所需串中,前一个串必是后一个串的后缀。具体而言,因为前一个串必定在后一个串中出现至少两次,于是我们就砍掉后一个串的后缀,使得前一个串的最后一次出现的结尾处即为后一个串的结尾。这样,被砍掉后缀的后一个串,仍然至少在后后一个串中出现两次,则此方案合法。

于是我们考虑在SAM上从上往下DP。设 \(f_i\) 表示节点 \(i\) 所对应的 \(\text{endpos}\) 集合中最长串所能套娃的层数。

设我们当前想从 \(f_x\) 转移到其某个儿子 \(f_y\)。明显,\(f_y\) 有两种可能,一是直接就等于 \(f_x\),另一种是等于 \(f_x+1\),此时是在 \(f_x\) 外面套了一层。

我们考虑找出 \(\text{endpos}(y)\) 中任意一个元素(因为其代表的串都是相同的,所以任选一个即可),设作 \(pos_y\)。则,在 \(pos_y\) 处,\(x\) 所对应的串已经出现过一次了,只需在剩下的位置中再出现一次即可。因为其必须完整地在 \(y\) 中出现,又不与后缀的那一次重合,所以其必须在区间 \(\big[pos_y-\text{len}(y)+\text{len}(x),pos_y-1\Big]\) 中出现。换言之,\(\text{endpos}(x)\) 中,至少要有一个元素在上述区间内。

考虑用线段树合并维护 \(\text{endpos}\) 集合即可。这也是某些题中的经典套路。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,cnt=1,rt[400100],f[400100],las[400100],pos[400100],res;
char s[200100];
struct Suffix_Automaton{int ch[26],len,fa;}t[400100];
int Add(int x,int c){
	int xx=++cnt;t[xx].len=t[x].len+1;
	for(;x&&!t[x].ch[c];x=t[x].fa)t[x].ch[c]=xx;
	if(!x){t[xx].fa=1;return xx;}
	int y=t[x].ch[c];
	if(t[y].len==t[x].len+1){t[xx].fa=y;return xx;}
	int yy=++cnt;t[yy]=t[y],t[yy].len=t[x].len+1;
	t[xx].fa=t[y].fa=yy;
	for(;x&&t[x].ch[c]==y;x=t[x].fa)t[x].ch[c]=yy;
	return xx;
}

#define mid ((l+r)>>1)
int tot;
struct SegmentTree{
	int lson,rson;
}seg[20010000];
int merge(int x,int y){
	if(!x||!y)return x+y;
	int z=++tot;
	seg[z].lson=merge(seg[x].lson,seg[y].lson);
	seg[z].rson=merge(seg[x].rson,seg[y].rson);
	return z;
}
void modify(int &x,int l,int r,int P){
	if(l>P||r<P)return;
	if(!x)x=++tot;
	if(l!=r)modify(seg[x].lson,l,mid,P),modify(seg[x].rson,mid+1,r,P);
}
bool query(int x,int l,int r,int L,int R){
	if(l>R||r<L||!x)return false;
	if(L<=l&&r<=R)return true;
	return query(seg[x].lson,l,mid,L,R)|query(seg[x].rson,mid+1,r,L,R);
}

vector<int>v[400100];
void dfs1(int x){for(auto y:v[x])dfs1(y),rt[x]=merge(rt[x],rt[y]),pos[x]=pos[y];}
void dfs2(int x){
	res=max(res,f[x]);
	for(auto y:v[x]){
		if(query(rt[las[x]],0,n-1,pos[y]-t[y].len+t[las[x]].len,pos[y]-1))f[y]=f[x]+1,las[y]=y;
		else f[y]=f[x],las[y]=las[x];
		dfs2(y);
	}
}
int main(){
	scanf("%d%s",&n,s);
	for(int i=0,las=1;i<n;i++)las=Add(las,s[i]-‘a‘),modify(rt[las],0,n-1,i),pos[las]=i;
	for(int i=2;i<=cnt;i++)v[t[i].fa].push_back(i);
	dfs1(1);
	for(auto y:v[1])f[y]=1,las[y]=y,dfs2(y);
	printf("%d\n",res);
	return 0;
}

CF700E Cool Slogans

标签:区间   %s   href   lan   query   集合   false   com   ace   

原文地址:https://www.cnblogs.com/Troverld/p/14605738.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!