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