【题解】CF700E Cool Slogans
时间:2021-08-28
本文章向大家介绍【题解】CF700E Cool Slogans ,主要包括【题解】CF700E Cool Slogans 使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
\(\text{Solution:}\)
\(dp,\) 思路都是对的 又死细节上了 对 SAM 的理解还是不够……(或者应该说是 \(dp\))
首先考虑一下什么情况我们可以接上一个串。题目给的是出现了两次,那转化到 SAM 上,我们如何用已知信息来判别?
首先证明一个结论,如果 \(t\) 可以接到当前串的后面,那必然存在一种最优方案使 \(s\) 是 \(t\) 的后缀。
若不是,考虑把后面多余的切掉不会劣。
那么就可以直接扔到 parent 树上考虑了。继续考虑在这种情况下我们如何判定。
假定我们已经求出了一个 \(t\) 的 endpos,
直接扔结论:
\[\exist i\in [endpos-len[t]+len[s],endpos-1],i\in endpos_s
\]
证明:首先其作为后缀已经出现了一次,剩下的一次需要在 \(endpos\) 之前,最右端也就是 \(endpos-1;\) 而左端至少要到起点加上其长度的位置。
那就可以愉快 \(dp\) 了,设 \(f[i]\) 表示以 parent 树上 \(i\) 节点结尾的最长长度,显然如果符合条件就直接 \(f[i]=f[x]+1.\) 不符合呢?
当时写的时候就默认给删了……实际上这样不对,显然舍去了一些答案呀……
观察一下,在树上一个后缀容易在深度更深的后缀中出现两次。
这也就意味着我们的直接舍去的做法是错的。所以我们还需要记录一下一个点的前驱状态。
初始的时候转移状态默认为自己,如果一个节点其父亲无法与当前点匹配,那就令其前驱状态为其父亲。
转移的时候均默认从前驱状态转移即可。复杂度是 \(O(n\log n),\) 因为需要线段树合并维护 endpos 来支持查询。
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
const int TN=1e7+10;
inline int Min(int x,int y){return x<y?x:y;}
inline int Max(int x,int y){return x>y?x:y;}
int n;
char s[N];
namespace SGT{
int ls[TN],rs[TN],node;
void change(int &x,const int &L,const int &R,const int &pos){
if(!x)x=++node;
if(L==R)return;
int mid=(L+R)>>1;
if(pos<=mid)change(ls[x],L,mid,pos);
else change(rs[x],mid+1,R,pos);
}
int merge(const int &x,const int &y){
if(!x||!y)return x|y;
int p=++node;
ls[p]=merge(ls[x],ls[y]);
rs[p]=merge(rs[x],rs[y]);
return p;
}
bool query(const int &x,const int &L,const int &R,const int &l,const int &r){
if(!x)return false;
if(L>=l&&R<=r)return true;
int mid=(L+R)>>1;
bool res=false;
if(l<=mid)res|=query(ls[x],L,mid,l,r);
if(mid<r)res|=query(rs[x],mid+1,R,l,r);
return res;
}
}
using namespace SGT;
namespace SAM{
int len[N],pa[N],ch[N][26],last=1,tot=1,col[N],minr[N],f[N],rt[N];
int siz[N],prestate[N];
vector<int>G[N];
queue<int>q;
void insert(const int &c,const int &cl){
int p=last;int np=++tot;last=tot;siz[np]=1;
len[np]=len[p]+1;col[np]=cl;minr[np]=cl;
for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
if(!p)pa[np]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1)pa[np]=q;
else{
int nq=++tot;
len[nq]=len[p]+1;
pa[nq]=pa[q];pa[q]=pa[np]=nq;
memcpy(ch[nq],ch[q],sizeof ch[q]);
for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
}
}
}
void dfs(int x){
if(col[x])change(rt[x],1,n,col[x]);
for(auto v:G[x]){
dfs(v);
rt[x]=merge(rt[x],rt[v]);
minr[x]=Max(minr[x],minr[v]);
siz[x]+=siz[v];
}
}
void Build(){
for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
dfs(1);
}
void Dp(){
q.push(1);f[1]=0;
while(!q.empty()){
int x=q.front();
q.pop();
for(auto v:G[x]){
int endpos=minr[v];
// printf("%d:[%d %d]\n",v,endpos-len[v]+len[x],endpos-1);
if(query(rt[prestate[x]],1,n,endpos-len[v]+len[prestate[x]],endpos-1))f[v]=Max(f[v],f[prestate[x]]+1);//,cout<<"?\n";
else {
f[v]=Max(f[v],f[x]),prestate[v]=prestate[x];
}
q.push(v);
}
}
}
}
using namespace SAM;
int ep[N];
int main(){
// freopen("in.txt","r",stdin);
scanf("%d",&n);
scanf("%s",s+1);
memset(minr,-0x3f,sizeof minr);
for(int i=1;i<=n;++i){
int v=s[i]-'a';
ep[i]=tot+1;
insert(v,i);
}
for(int i=1;i<=tot;++i)f[i]=1,prestate[i]=i;
Build();
// for(int i=1;i<=n;++i){
// printf("%d %d %d\n",ep[i],siz[ep[i]],minr[ep[i]]);
// }
// for(int i=1;i<=tot;++i)cout<<minr[i]<<" ";
// cout<<endl;
Dp();
// for(int i=1;i<=tot;++i)cout<<f[i]<<" ";
// puts("");
int ans=-1;
for(int i=1;i<=tot;++i)ans=Max(ans,f[i]);
printf("%d\n",ans);
return 0;
}
原文地址:https://www.cnblogs.com/h-lka/p/15201093.html
- Mysql备份系列(4)--lvm-snapshot备份mysql数据(全量+增量)操作记录
- 免费体验国内首款写作机器人,就是这么溜!
- Python学习笔记(3):数据集操作-列的统一操作
- Python学习笔记(3):数据集操作-列的统一操作
- Python学习笔记(4):自定义时间类
- Mysql备份系列(3)--innobackupex备份mysql大数据(全量+增量)操作记录
- 微信小程序开发:设置消息推送
- Mysql备份系列(1)--备份方案总结性梳理
- ASP.NET中常用的优化性能的方法(转贴,Icyer收集整理)
- 顶象全场景IoT安全方案解决物联网两大难题
- 诡异的【session丢失】和【<img src="">标签】
- TensorFlow核心使用要点
- “搜一搜”直达生活服务 微信连接移动消费新场景
- Linux服务器安全登录设置记录
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法