CF700E-Cool Slogans【SAM,线段树合并,dp】

1|0正题

题目链接:https://www.luogu.com.cn/problem/CF700E


1|1题目大意

给出一个字符串S,求一个最大的k使得存在k个字符串其中s1S的子串,si+1si中出现了至少2次。


1|2解题思路

首先我们需要有两个结论

  1. si+1一定是si的其中一个后缀。因为如果si+1不是si的一个后缀,那么si去掉后面那一部分不会影响匹配数并且更短,也就是更优
  2. 对于parents树上的一对父子x,yy代表的所以字符串与x最长串的匹配数均相等。因为如果有不等的,那么证明y中的字符串的出现endpos集合不同,不符合SAM的定义,故不成立。

这样我们就可以在SAM上进行dp了,因为第一个结论我们可以直接在fail树上dp,然后第二个结论让我们能够使用每个节点最长的串来进行匹配,因为这不会影响答案。

现在我们需要考虑如何判断一个节点的串是否在另一个它的祖先节点的串中出现了两次,首先作为后缀已经出现了一次,然后只需要判断是否包含一个出现在[posxlenx+leny,posx1]的串就好了,因为endpos在这个范围内出现的串一定是与字符串x相同的。

用线段树合并维护每个节点包含的串的位置就好了,还有就是要从上往下转移。

时间复杂度O(nlogn)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=4e5+10,M=N<<6; int n,cnt,last,ans,c[N],p[N],f[N],top[N]; int rt[N],pos[N],len[N],fa[N],ch[N][26]; char s[N]; struct Seq_Tree{ int w[M],ls[M],rs[M],cnt; int Change(int x,int L,int R,int pos){ int y=++cnt;w[y]=w[x]+1; if(L==R)return y; int mid=(L+R)>>1; if(pos<=mid)ls[y]=Change(ls[x],L,mid,pos),rs[y]=rs[x]; else ls[y]=ls[x],rs[y]=Change(rs[x],mid+1,R,pos); return y; } int Merge(int x,int y,int L,int R){ if(!x||!y)return x|y; int p=++cnt;w[p]=w[x]+w[y]; if(L==R)return p; int mid=(L+R)>>1; ls[p]=Merge(ls[x],ls[y],L,mid); rs[p]=Merge(rs[x],rs[y],mid+1,R); w[p]=w[ls[p]]+w[rs[p]]; return p; } int Ask(int x,int L,int R,int l,int r){ if(!x)return 0; if(L==l&&R==r)return w[x]; int mid=(L+R)>>1; if(r<=mid)return Ask(ls[x],L,mid,l,r); if(l>mid)return Ask(rs[x],mid+1,R,l,r); return Ask(ls[x],L,mid,l,mid)+Ask(rs[x],mid+1,R,mid+1,r); } }T; void Insert(int c){ int p=last,np=last=++cnt; len[np]=len[p]+1; for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np; if(!p)fa[np]=1; else{ int q=ch[p][c]; if(len[p]+1==len[q])fa[np]=q; else{ int nq=++cnt;len[nq]=len[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[nq])); fa[nq]=fa[q];pos[nq]=pos[q];fa[q]=fa[np]=nq; for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq; } } return; } int main() { scanf("%d",&n);cnt=last=1; scanf("%s",s+1); for(int i=1;i<=n;i++) Insert(s[i]-'a'),rt[last]=T.Change(rt[last],1,n,i),pos[last]=i; for(int i=1;i<=cnt;i++)c[len[i]]++; for(int i=1;i<=n;i++)c[i]+=c[i-1]; for(int i=1;i<=cnt;i++)p[c[len[i]]--]=i; for(int i=cnt;i>1;i--) rt[fa[p[i]]]=T.Merge(rt[fa[p[i]]],rt[p[i]],1,n); int ans=1; for(int i=2;i<=cnt;i++){ int x=p[i],y=fa[x]; if(y==1){f[x]=1;top[x]=x;continue;} y=top[y]; if(T.Ask(rt[y],1,n,pos[x]-len[x]+len[y],pos[x]-1)) f[x]=f[y]+1,top[x]=x; else top[x]=y,f[x]=f[y]; ans=max(ans,f[x]); } printf("%d\n",ans); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/14266819.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(61)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示