[CF1063F]String Journey
题意:定义长度为$k$的journey为一个字符串序列$t_{1\cdots k}$,对$\forall i\gt1$满足$t_i$是$t_{i-1}$的严格子串,定义字符串$s$上的journey为$t_{1\cdots k}$在$s$中依次不重叠出现的journey,给定$s$,求$s$上的最长journey
首先,如果我们能找到长度为$k$的journey,那么一定存在一个长度为$k$的journey满足$|t_i|=|t_{i-1}|-1$,这个直接在原journey上删一些字符即可
然后有一条性质:如果一个长度为$k$的journey满足$t_1$从$s_i$开始,那么对$\forall j\leq k$,存在长度为$j$的journey满足$t_1$从$s_i$开始,这个同样可以在原journey中删字符得到
所以考虑DP:设$f_i$表示最长的journey使得$t_1$从$s_i$开始,枚举$t_1=s_{i\cdots i+k-1}$,得到转移$f_i=\max\left\{k|\exists j\geq i+k,f_j\geq k-1,s_{j\cdots j+k-2}\subset s_{i\cdots i+k-1}\right\}$
如果知道怎么解决后面的限制,那么我们可以用二分来求$f_i$,需要$O(n\log n)$次check,但因为$f_{i+1}\geq f_i-1$(如果$f_i$表示的journey满足$t_2$在$t_1$的开始匹配位置是$s_i$,那么$f_{i+1}$就可能$-1$),所以我们可以从$f_{i+1}+1$开始往下check,总共调用$O(n)$次check
现在来看怎么check,字符串的条件是说$s_{j\cdots j+k-2}=s_{i\cdots i+k-2}$或$s_{i+1\cdots i+k-1}$,而子串在后缀数组上对应着一个区间,所以我们可以把$f_i$赋到$rank_i$上,在线段树上查询区间最大值即可,还有一个条件$j\geq i+k$,但很容易发现满足条件的$j$其实是随着$i$减小而单调不增的($i$不变时$k$在不断减小,$i$减少$1$的同时$k$只能增加$1$),所以在线段树上动态插入即可,单次check的时间复杂度为$O(\log n)$
总时间复杂度为$O(n\log n)$,是很优美的复杂度
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; char s[500010]; int rk[1000010],sa[500010],c[500010],h[500010],mn[500010][19],lg[500010],n; struct pr{ int c[2],i; pr(int a=0,int b=0,int d=0){c[0]=a;c[1]=b;i=d;} }p[500010],q[500010]; bool operator!=(pr a,pr b){return a.c[0]!=b.c[0]||a.c[1]!=b.c[1];} void sort(int f){ int i,m; memset(c,0,sizeof(c)); m=0; for(i=1;i<=n;i++){ c[p[i].c[f]]++; m=max(m,p[i].c[f]); } for(i=1;i<=m;i++)c[i]+=c[i-1]; for(i=n;i>0;i--)q[c[p[i].c[f]]--]=p[i]; memcpy(p,q,sizeof(q)); } void suf(){ int i,j,l,m; for(i=1;i<=n;i++)rk[i]=s[i]-'a'+1; for(l=1;l<=n;l<<=1){ for(i=1;i<=n;i++)p[i]=pr(rk[i],rk[i+l],i); sort(1); sort(0); for(i=1,m=0;i<=n;i++){ if(p[i]!=p[i-1])m++; rk[p[i].i]=m; } } for(i=1;i<=n;i++)sa[rk[i]]=i; for(i=1,l=0;i<=n;i++){ if(l)l--; while(s[i+l]==s[sa[rk[i]-1]+l])l++; h[rk[i]]=l; } for(i=1;i<=n;i++)mn[i][0]=h[i]; for(j=1;j<19;j++){ for(i=1;i+(1<<j)-1<=n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]); } for(i=2;i<=n;i++)lg[i]=lg[i>>1]+1; } int qmin(int l,int r){ int k=lg[r-l+1]; return min(mn[l][k],mn[r-(1<<k)+1][k]); } int mx[2097152],M; void modify(int x,int v){ if(!x)return; x+=M; mx[x]=max(mx[x],v); for(x>>=1;x;x>>=1)mx[x]=max(mx[x<<1],mx[x<<1|1]); } int query(int s,int t){ int res=-1; for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){ if(~s&1)res=max(res,mx[s^1]); if(t&1)res=max(res,mx[t^1]); } return res; } int submax(int s,int k){ int l,r,mid,ans,lp,rp; if(k==0)return mx[1]; ans=s; l=1; r=s-1; while(l<=r){ mid=(l+r)>>1; if(qmin(mid+1,s)>=k){ ans=mid; r=mid-1; }else l=mid+1; } lp=ans; ans=s; l=s+1; r=n; while(l<=r){ mid=(l+r)>>1; if(qmin(s+1,mid)>=k){ ans=mid; l=mid+1; }else r=mid-1; } rp=ans; return query(lp,rp); } int f[500010]; int main(){ int i,j,ans; scanf("%d%s",&n,s+1); suf(); memset(mx,-1,sizeof(mx)); for(M=1;M<=n+2;M<<=1); modify(n+1,0); ans=0; for(i=n;i>0;i--){ for(j=f[i+1]+1;;j--){ modify(rk[i+j],f[i+j]); if(max(submax(rk[i],j-1),submax(rk[i+1],j-1))>=j-1)break; } f[i]=j; ans=max(ans,f[i]); } printf("%d",ans); }
[CF1063F]String Journey的更多相关文章
- [CF1063F]String Journey[后缀数组+线段树]
题意 在 \(S\) 中找出 \(t\) 个子串满足 \(t_{i+1}\) 是 \(t_{i}\) 的子串,要让 \(t\) 最大. \(|S| \leq 5\times 10^5\). 分析 定义 ...
- 解题:CF1063F String Journey
题面 分析性质以进行DP 性质1:一定有一个最优解通过每次删除第一个或最后一个字符达到 这个脑补一下就能证明了 那么我们设$dp[i]$表示后缀$[i,n]$选出一个前缀所能达到的最大长度,从右往左D ...
- CF1063F. String Journey(后缀数组+线段树)
题目链接 https://codeforces.com/contest/1063/problem/F 题解 虽然本题有时间复杂度较高但非常好写的做法...... 首先,若答案为 \(k\),则一定存在 ...
- CF1063F String Journey DP、SAM、线段树
传送门 为了方便把串反过来,条件变为\(t_i\)是\(t_{i+1}\)的真子串,答案显然不变. 一件重要的事情是必定存在一种最优解,字符串序列\(\{t\}\)满足\(|t_i| = i\). 考 ...
- 【CF1063F】String Journey 哈希
题目大意 给你一个字符串 \(s\),让你找出最大的 \(k\),满足:能从 \(s\) 中选出 \(k\) 个不重叠的字符串 \(t_1,t_2,\ldots,t_k\),且 \(\forall i ...
- Codeforces 1063F - String Journey(后缀数组+线段树+dp)
Codeforces 题面传送门 & 洛谷题面传送门 神仙题,做了我整整 2.5h,写篇题解纪念下逝去的中午 后排膜拜 1 年前就独立切掉此题的 ymx,我在 2021 年的第 5270 个小 ...
- CF721C. Journey[DP DAG]
C. Journey time limit per test 3 seconds memory limit per test 256 megabytes input standard input ou ...
- A Knight's Journey 分类: POJ 搜索 2015-08-08 07:32 2人阅读 评论(0) 收藏
A Knight's Journey Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 35564 Accepted: 12119 ...
- 【推公式】UVa 10995 - Educational Journey
1A~,但后来看人家的代码好像又写臭了,T^T... Problem A: Educational journey The University of Calgary team qualified f ...
随机推荐
- 获取应用版本号,版本名称,包名,AppName,图标,是否是系统应用,获取手机中所有应用,所有进程
PackageManager packageManager = getPackageManager(); PackageInfo packageInfo; = packageManager.getPa ...
- 火狐浏览器下点击a标签时出现虚线的解决方案
1.兼容性问题 火狐浏览器下点击a标签时出现虚线 2.解决方案 a:focus { outline: none;}
- 真正的上锁前,为何要调用preempt_disable()来关闭抢占的case【转】
转自:http://blog.csdn.net/kasalyn/article/details/11473885 static inline void raw_spin_lock(raw_spinlo ...
- 2015多校第9场 HDU 5405 Sometimes Naive 树链剖分
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5405 题意: 给你一棵n个节点的树,有点权. 要求支持两种操作: 操作1:更改某个节点的 ...
- JavaScript知识点的总结
一.Javascript的含义 是一种解释性的语言,主要给网页添加各色各样的动态功能,同时为用户提供浏览效果. 二.JavaScript的主要特点 1. 简单性 2. 动态性 3. 安全性 4. 跨平 ...
- mac pro 安装mysql并且配置my.cnf(添加默认字符集utf8,数据存放路径,修改已经建好的表的默认字符集等)、mac mysql my.cnf路径
如果你是还没有下载安装文件,请到官网下载http://dev.mysql.com/downloads/mysql/ 下载好mysql的mac版本的安装文件后解压后将文件放到目录 /usr/local/ ...
- Python调用shell
今天学习了Python通过子进程调用shell,感觉教程上讲的过于繁复,有一些根本没用的功能,比如重定向输入输出,这个shell本身就支持,没有必要用Python的api.决定自己总结下. 其实总的来 ...
- POJ-2778
DNA Sequence Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 12726 Accepted: 4862 Des ...
- ActiveMQ基于JMS的pub/sub传播机制
原文地址:[ActiveMQ实战]基于JMS的pub/sub传播机制 发布订阅模型 就像订阅报纸,我们可以选择一份或者多份报纸.比如:北京日报.人民日报.这些报纸就相当于发布订阅模型中的topic.如 ...
- resteasy json
https://www.cnblogs.com/toSeeMyDream/p/5763725.html