[CF1063F]String Journey[后缀数组+线段树]
题意
在 \(S\) 中找出 \(t\) 个子串满足 \(t_{i+1}\) 是 \(t_{i}\) 的子串,要让 \(t\) 最大。
\(|S| \leq 5\times 10^5\).
分析
定义状态 \(f_{i}\) 表示从 \(i\) 出发能够得到的最长的 \(journey\) .
容易得到最终的答案最右边的串长度一定可以是1.
同时如果删掉没用的部分过后 \(t_i\) 的长度一定可以为 $t_{i+1} +1 $.
如果在 \(i\) 位置存在长度为 \(k\) 的答案的话,将两边某一个字符在所有串中抠掉(还要舍去一个串),一定也存在长度为 \(k-1\) 的答案,所以答案单调。
假设当前枚举的答案为 \(k\) ,只需要在 \([i+k,n]\) 这个区间中存在一个子串满足
\]
同时 \(f_j\geq k-1\) 的话,就说明 \(f_i\geq k\) .
但是发现一定有 \(f_i\leq f_{i+1}+1\) ,所以暴力枚举每个位置的答案,不需要二分。
那些满足 \(LCP(i,j) \geq k-1\) 的位置在 \(sa\) 数组中一定是一个区间,线段树维护最大值。
总时间复杂度为 \(O(nlogn)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].lst,v=e[i].to)
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define repd(i,a,b) for(int i=a;i>=b;--i)
#define pb push_back
typedef long long LL;
inline int gi(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
const int N=5e5 + 7;
int n,ans;
char s[N];
int val[N<<2],f[N];
#define Ls o<<1
#define Rs o<<1|1
void modify(int p,int l,int r,int o,int v){
Max(val[o],v);
if(l==r) return;
int mid=l+r>>1;
if(p<=mid) modify(p,l,mid,Ls,v);
else modify(p,mid+1,r,Rs,v);
}
int query(int L,int R,int l,int r,int o){
if(L<=l&&r<=R) return val[o];
int mid=l+r>>1;
if(R<=mid) return query(L,R,l,mid,Ls);
if(L>mid) return query(L,R,mid+1,r,Rs);
return max(query(L,R,l,mid,Ls),query(L,R,mid+1,r,Rs));
}
namespace SA{
int x[N],y[N],c[N],sa[N],h[N],mi[N][20],Log[N];
void getsa(int m){
rep(i,1,m) c[i]=0;
rep(i,1,n) c[x[i]=s[i]]++;
rep(i,1,m) c[i]+=c[i-1];
repd(i,n,1) sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int p=0;
for(int i=n;i>=n-k+1;--i) y[++p]=i;
rep(i,1,n) if(sa[i]>k) y[++p]=sa[i]-k;
rep(i,1,m) c[i]=0;
rep(i,1,n) c[x[y[i]]]++;
rep(i,1,m) c[i]+=c[i-1];
repd(i,n,1) sa[c[x[y[i]]]--]=y[i];
swap(x,y);p=1;x[sa[1]]=1;
rep(i,2,n)
x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p;
if(p>=n) break; m = p;
}
rep(i,1,n) x[sa[i]]=i;
for(int i=1,j=0;i<=n;++i){
if(j) --j;if(x[i]==1) continue;
while(s[i+j]==s[sa[x[i]-1]+j]) ++j;
h[x[i]]=j;
}
Log[1]=0;
rep(i,2,n) Log[i]=Log[i>>1]+1;
rep(i,1,n) mi[i][0]=h[i];
for(int k=1;1<<k<=n;++k)
for(int i=1;i+(1<<k)-1<=n;++i)
mi[i][k]=min(mi[i][k-1],mi[i+(1<<k-1)][k-1]);
}
int rmq_query(int l,int r){
l++;
if(l>r) return n+1;
int k=Log[r-l+1];
return min(mi[l][k],mi[r-(1<<k)+1][k]);
}
int get1(int p,int up){
int l=1,r=p;
while(l<r){
int mid=l+r>>1;
if(rmq_query(mid,p)>=up) r=mid;
else l=mid+1;
}
return l;
}
int get2(int p,int up){
int l=p,r=n;
while(l<r){
int mid=l+r+1>>1;
if(rmq_query(p,mid)>=up) l=mid;
else r=mid-1;
}
return l;
}
}
int main(){
scanf("%d%s",&n,s+1);
using namespace SA;
getsa(129);
f[n]=ans=1;
for(int i=n-1,j=1;i;--i){
for(++j;j;--j){
if(i+j<=n) modify(x[i+j],1,n,1,f[i+j]);
int l=get1(x[i+1],j-1),r=get2(x[i+1],j-1),fg=0;
fg|=query(l,r,1,n,1)>=j-1;
l=get1(x[i],j-1),r=get2(x[i],j-1);
fg|=query(l,r,1,n,1)>=j-1;
if(fg) break;
}
f[i]=j;
Max(ans,f[i]);
}
printf("%d\n",ans);
return 0;
}
[CF1063F]String Journey[后缀数组+线段树]的更多相关文章
- Codeforces 1063F - String Journey(后缀数组+线段树+dp)
Codeforces 题面传送门 & 洛谷题面传送门 神仙题,做了我整整 2.5h,写篇题解纪念下逝去的中午 后排膜拜 1 年前就独立切掉此题的 ymx,我在 2021 年的第 5270 个小 ...
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...
- 【XSY1551】往事 广义后缀数组 线段树合并
题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\) ...
- Luogu4770 NOI2018你的名字(后缀数组+线段树)
即求b串有多少个本质不同的非空子串,在a串的给定区间内未出现.即使已经8102年并且马上就9102年了,还是要高举SA伟大旗帜不动摇. 考虑离线,将所有询问串及一开始给的串加分隔符连起来,求出SA.对 ...
- BZOJ 2865 字符串识别 | 后缀数组 线段树
集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...
- bzoj 1396: 识别子串 && bzoj 2865: 字符串识别【后缀数组+线段树】
根据height数组的定义,和当前后缀串i最长的相同串的长度就是max(height[i],height[i+1]),这个后缀贡献的最短不同串长度就是len=max(height[i],height[ ...
- BZOJ 2865 字符串识别(后缀数组+线段树)
很容易想到只考虑后缀长度必须为\(max(height[rk[i]],height[rk[i]+1])+1\)(即\([i,i+x-1]\)代表的串只出现过一次)然后我正着做一遍反着做一遍,再取一个\ ...
- [CF653F] Paper task - 后缀数组,线段树,vector
[CF653F] Paper task Description 给定一个括号序列,统计合法的本质不同子串的个数. Solution 很容易想到,只要在传统统计本质不同子串的基础上修改一下即可. 考虑经 ...
- BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)
题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...
随机推荐
- 如何用 Python 实现 Web 抓取?
[编者按]本文作者为 Blog Bowl 联合创始人 Shaumik Daityari,主要介绍 Web 抓取技术的基本实现原理和方法.文章系国内 ITOM 管理平台 OneAPM 编译呈现,以下为正 ...
- EmEditor的一个好用的正则替换功能
最近在编辑文本的时候用到了EmEditor的一个好用的正则替换功能.即我想用搜索到内容的一部分来生成另一段文本.例如客户提供给我一大堆MYSQL的建立主键的脚本,我想改成MSSQL的建立主键的脚本,这 ...
- VUE中v-on:click事件中获取当前dom元素
在开发中总是忘记, 特意在此记录 关键字: $event <div class="bed" v-on:click="updateBed(index,$e ...
- 使用Amanda ZRM备份远程MySQL数据库
本文写道最后的时候,我才发现ZRM for MySQL的一个致命问题,就我目前的理解和测试来看,它恢复数据的时候是采取覆盖的方式,举个例子,假定某台数据库服务器上有两个数据库test1,test2,你 ...
- InfoPath读取数据库
public void LoadBtn_Clicked(object sender, ClickedEventArgs e) { // 配置连接字符串 using (SqlConnection con ...
- Python邮件发送源码
-- coding:utf-8 -- i = 0 while i < 10: #发送十次 import smtplib from email.mime.text import MIMEText ...
- 【爬坑】Python 3.6 在 Socket 编程时出现类型错误 TypeError: a bytes-like object is required, not 'str'
1. 问题描述 Python 3.6 在 Socket 编程时出现错误如下 Traceback (most recent call last): File "F:/share/IdeaPro ...
- Linux 配置 hosts
1. hosts 是什么 维基百科对 hosts 的介绍如下: hosts文件(域名解析文件)是一个用于储存计算机网络中各节点信息的计算机文件. 这个文件负责将主机名称映射到相应的IP地址. host ...
- 匿名访问windows server 2008 R2 文件服务器的共享
匿名访问windows server 2008 R2 文件服务器的共享 匿名访问windows 2008 R2 文件服务器的共享,七步:第一步 取消简单文件共享:第二步 设置需要共享的文件夹every ...
- 51nod 贪心算法题集
2070 最小罚款: 题意:初始有n元,每个任务有2个参数:t和w,<=t时刻前完成任务才可避免造成损失w.问:如何安排才能尽可能避免损失?一个任务执行时间是一个单位时间. 分析:任务按时间排个 ...