首页 > 其他分享 >CF700E Cool Slogans

CF700E Cool Slogans

时间:2022-10-06 15:44:07浏览次数:37  
标签:ch int text len put Slogans np CF700E Cool

CF700E Cool Slogans

首先可以得到以下性质。

性质1

当 \(k\) 最大时,一定存在一组 \(s\) 满足 \(s_{i}\) 为 \(s_{i+1}\) 的后缀(\(1 \le i < k\))。

证明:设存在一组解使得 \(s_{i}\) 不是 \(s_{i+1}\) 的后缀,则将 \(s_i\) 第二次出现在 \(s_{i+1}\) 后面的部分删去仍能满足条件,且使得 \(s^\prime_{i+1}\) 成为 \(s_{i+1}\) 的一个前缀,仍能够在 \(s_{i+2}\) 中出现至少 \(2\) 次。对于任意的 \(i\) 均可以进行以上操作,最终一定可以满足原命题,证毕。

性质2

对任意字符串 \(s\) 建立后缀自动机与 \(\text{parent}\) 树后,设 \(f\) 为 \(x\) 在树上的祖先,则一定有 \(f\) 代表的任意一个串在 \(x\) 代表的最长串中出现次数相同。

证明:依然使用反证法。设 \(s1,s2\) 均为 \(f\) 节点代表的串(\(|s1|>|s2|\)),\(t\) 为 \(x\) 代表的最长串。当 \(s1\) 与 \(s2\) 在 \(t\) 中出现次数不同时,必然为 \(s1\) 某个开头在 \(t\) 串之前而 \(s2\) 开头在 \(t\) 串上,如图所示。记 \(s1\) 在 \(t\) 串开头之前的部分为 \(p\),则 \(p+t\) 与 \(t\) 的 \(\text{endpos}\) 相同,则 \(|p+t|>|t|\),与题设矛盾,证毕。

现在可以发现原题中选择的 \(s\) 是 \(\text{parent}\) 树上一条链的部分节点,且若选择了节点 \(x\),则一定会选择 \(x\) 所代表的最长串(由性质 \(2\) 可得)。

考虑 \(\text{dp}\),设 \(f_{x}\) 表示从根节点开始选到 \(x\)(必须选择 \(x\))的序列最大长度。设 \(p\) 为 \(x\) 的祖先且满足 \(p\) 中最长串在 \(x\) 中出现至少 \(2\) 次,则有

\[f_{x}=\max(f_p)+1 \]

当前匹配到点 \(x\) 时,若 \(x\) 能够选进答案则一定会选。

证明:设 \(x\) 代表最长串为 \(s\),\(x\) 子树内某个选择的转移点代表的最长串为 \(t\)。容易得到 \(s\) 一定是 \(t\) 的后缀。由于 \(x\) 出现次数多于 \(t\),则 \(s\) 在之后的匹配也一定更优,证毕。

则我们只需要维护最后一个转移点 \(g_x\) 即可。

考虑如何判断 \(x\) 是否能够被选择。设最后一个转移点为 \(p\),\(x\) 代表串为 \(s\),\(p\) 代表串为 \(t\),由于 \(p\) 为 \(x\) 的祖先,即 \(t\) 为 \(s\) 后缀,则 \(s\) 出现时 \(t\) 也一定出现了。由于对于任何一次 \(s\) 出现时,\(t\) 都会出现,考虑维护任意一个 \(s\) 出现的段即可。当 \(t\) 出现在 \(s\) 串中且结尾不是 \(s\) 的结尾时,即为 \(t\) 在 \(s\) 中出现至少两次。

使用线段树合并维护 \(\text{endpos}\) 集合,在线段树中查询区间 \([\text{pos}_x-\text{len}_x+\text{len}_p,\text{pos}_x-1]\) 是否有值即可。时间复杂度 \(\mathcal{O}(n \log n)\)。

#include<bits/stdc++.h>
using namespace std;
namespace IO{
	template<typename T>inline bool read(T &x){
		x=0;
		char ch=getchar();
		bool flag=0,ret=0;
		while(ch<'0'||ch>'9') flag=flag||(ch=='-'),ch=getchar();
		while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(),ret=1;
		x=flag?-x:x;
        return ret;
	}
	template<typename T,typename ...Args>inline bool read(T& a,Args& ...args){
	    return read(a)&&read(args...);
	}
	template<typename T>void prt(T x){
		if(x>9) prt(x/10);
		putchar(x%10+'0');
	}
	template<typename T>inline void put(T x){
		if(x<0) putchar('-'),x=-x;
		prt(x);
	}
	template<typename T>inline void put(char ch,T x){
		if(x<0) putchar('-'),x=-x;
		prt(x);
		putchar(ch);
	}
	template<typename T,typename ...Args>inline void put(T a,Args ...args){
	    put(a);
		put(args...);
	}
	template<typename T,typename ...Args>inline void put(const char ch,T a,Args ...args){
	    put(ch,a);
		put(ch,args...);
	}
	inline void put(string s){
		for(int i=0,sz=s.length();i<sz;i++) putchar(s[i]);
	}
	inline void put(const char* s){
		for(int i=0,sz=strlen(s);i<sz;i++) putchar(s[i]);
	}
}
using namespace IO;
#define N 400005
struct Segment{
	struct node{
		int ls,rs,val;
	}t[N<<5];
	#define lc(x) t[x].ls
	#define rc(x) t[x].rs
	int idx;
	inline void push_up(int x){
		t[x].val=t[lc(x)].val|t[rc(x)].val;
	}
	inline void update(int &x,int l,int r,int pos){
		if(!x) x=++idx;
		if(l==r) return t[x].val=1,void();
		int mid=l+r>>1;
		if(pos<=mid) update(lc(x),l,mid,pos);
		else update(rc(x),mid+1,r,pos);
		push_up(x);
	}
	inline int query(int x,int l,int r,int ql,int qr){
		if(!x||ql>r||qr<l||ql>qr) return 0;
		if(ql<=l&&qr>=r) return t[x].val;
		int mid=l+r>>1;
		return query(lc(x),l,mid,ql,qr)|query(rc(x),mid+1,r,ql,qr);
	}
	inline int merge(int x,int y,int l,int r){
		if(!x||!y) return x|y;
		int z=++idx,mid=l+r>>1;
		if(l==r) return t[z].val=t[x].val|t[y].val,z;
		lc(z)=merge(lc(x),lc(y),l,mid);
		rc(z)=merge(rc(x),rc(y),mid+1,r);
		push_up(z);
		return z;
	}
	#undef lc
	#undef rc
}T;
int ans=0,n;
struct Suffix_Automaton{
	int pos[N],id[N],c[N],rt[N],f[N],g[N],idx,lst;
	struct node{
		int len,fa,ch[26];
	}t[N];
	Suffix_Automaton(){idx=lst=1;}
	inline void extend(int c){
		int p=lst,np=lst=++idx;
		t[np].len=t[p].len+1,pos[np]=t[np].len;
		T.update(rt[np],1,n,t[np].len);
		for(;p&&!t[p].ch[c];p=t[p].fa) t[p].ch[c]=np;
		if(!p) t[np].fa=1;
		else{
			int q=t[p].ch[c];
			if(t[p].len+1==t[q].len) t[np].fa=q;
			else{
				int nq=++idx;
				t[nq]=t[q],t[nq].len=t[p].len+1;
				t[np].fa=t[q].fa=nq,pos[nq]=pos[q];
				for(;p&&t[p].ch[c]==q;p=t[p].fa) t[p].ch[c]=nq;
			}
		}
	}
	inline void topo_sort(){
		for(int i=1;i<=idx;i++) c[t[i].len]++;
		for(int i=1;i<=idx;i++) c[i]+=c[i-1];
		for(int i=1;i<=idx;i++) id[c[t[i].len]--]=i;
		for(int i=idx;i;i--){
			int f=t[id[i]].fa,x=id[i];
			rt[f]=T.merge(rt[f],rt[x],1,n);
		}
		for(int i=2;i<=idx;i++){
			int fa=t[id[i]].fa,x=id[i];
			if(fa==1) f[x]=1,g[x]=x;
			else{
				if(T.query(rt[g[fa]],1,n,pos[x]-t[x].len+t[g[fa]].len,pos[x]-1)) f[x]=f[g[fa]]+1,g[x]=x;
				else f[x]=f[fa],g[x]=g[fa];
			}
			ans=max(ans,f[x]);
		}
	}
}S;
char s[N];
int main(){
	read(n),scanf("%s",s+1);
	for(int i=1;i<=n;i++) S.extend(s[i]-'a');
	S.topo_sort();
	put('\n',ans);
	return 0;
}

标签:ch,int,text,len,put,Slogans,np,CF700E,Cool
From: https://www.cnblogs.com/fzj2007/p/16757747.html

相关文章

  • cool
    OldEnglishcol"notwarm"(butusuallynotassevereascold),"moderatelycold,neitherwarmnorverycold,"also,figuratively,ofpersons,"unperturbed,un......
  • cookie获取的范围有多大、Coolie特点和作用
    cookie获取的范围有多大cookie获取范围多大?假设在一个tomcat服务器中,部署了多个web项目,那么这些web项目中cooklie能不能共享默认情况下cookie......