CF700E Cool Slogans

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)\)

code
#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;
}
posted @ 2022-10-06 15:38  fzj2007  阅读(22)  评论(0编辑  收藏  举报