首页 > 其他分享 >「题解」Codeforces 1063F String Journey

「题解」Codeforces 1063F String Journey

时间:2023-08-23 11:23:03浏览次数:32  
标签:typedef ch String int 题解 len fa Journey include

先 reverse 一下。

不难看出选出的字符串长度为 \(1,2,\cdots,k\) 一定不劣,仅考虑这种形式的。

然后考虑一手 dp,设 \(f_{i}\) 表示最后一个子串是 \(i\) 为结尾,最长长度是多少。

这样转移就是 \(f_i\gets f_{j}+1,iff\ s[j-f_j+1,j]\text { is } s[i-f_j,i] \text{'s substring}\).

掏出了一个结论,\(f_{i+1}\leq f_i+1\),因为不成立的话 \(f_i\) 一定可以调整得更大。

那么问题就可以从求解变成判定。具体就是初始时 \(i=k=1\),判定 \(f_i\) 是否 \(\geq k\),如果成功则 \(i,k\) 都 \(+1\),否则只有 \(k\gets k-1\),然后继续判定。总共判定 \(\mathcal{O}(n)\) 次。

那么现在问题就是判定 \([i-k+1,i]\) 能否选出,一种情况是上一个字符串 \(t\) 是 \(s[i-k+1,i-1]\),另一种情况是 \(s[i-k+2,i]\),注意到每次 \(i,k\) 同时 \(+1\) 相当于右端点向右一步;\(k\gets k-1\) 的时候相当于左端点向右一步。那么可以直接动态地维护这两个串在 SAM 上的位置,右端点向右一步就直接走自动机上的边,左端点向右一步就看看 \(len\) 是否还在当前节点的 \(len\) 范围中,如果不在就跳一步 parent 树上的父亲。

定位出这个节点之后,考虑需要判断是否存在上一个选出的串的右端点 \(j\),其合法要满足 \(f_j\geq k-1\) 且 \(j\leq i-k\) 且 \(t\) 是 \(s[j-f_j+1,j]\) 的后缀.

注意到每次 \(i,k\) 变化时是同时 \(+1\) 或者 \(k\gets k-1\),那么 \(i-k\) 是不降的。于是扫 \(i,k\) 的时候再每次把 \(f_{i-k}\) 给 SAM 上 \(s[i-k-f_{i-k}+1,i-k]\) 所代表的节点单点 \(\text{chkmax}\),查询的时候就查 parent 子树 \(\max\),就可以了。这样总的时间复杂度是 \(\mathcal{O}(n\log n)\).

真的有必要这样吗?

仔细想一想,由于子树中的等价类 \(len\) 一定大于当前节点的 \(len\),所以子树中一旦出现了一个单点 \(\text{chkmax}\),那么自己肯定是合法的,所以只需要记录出每个点子树中(不包含自己)是否已经出现过点,记作 \(ok\),并且记录每个点处的 \(f\) 的 \(\max\),判断合法时直接判 \(f\geq k\) 或者 \(ok\) 已经是否已经被标记过即可。扫 \(i,k\) 的时候加入 \(f_{i-k}\) 时,暴力不断在 parent 树上跳 father 更新 \(ok\) 即可,如果之前已经跳到过了就不再跳了(一个点被标记那么其祖先已均被标记)。这样均摊下来总的时间复杂度是 \(\mathcal{O}(n)\).

#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<random>
#include<assert.h>
#include<functional>
#define pb emplace_back
#define mp make_pair
#define fi first
#define se second
#define dbg(x) cerr<<"In Line "<< __LINE__<<" the "<<#x<<" = "<<x<<'\n'
#define dpi(x,y) cerr<<"In Line "<<__LINE__<<" the "<<#x<<" = "<<x<<" ; "<<"the "<<#y<<" = "<<y<<'\n'
#define DE(fmt,...) fprintf(stderr, "Line %d : " fmt "\n",__LINE__,##__VA_ARGS__)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
typedef pair<ll,int>pli;
typedef pair<ll,ll>pll;
typedef pair<int,ll>pil;
typedef vector<int>vi;
typedef vector<ll>vll;
typedef vector<pii>vpii;
typedef vector<pll>vpll;
template<typename T>T cmax(T &x, T y){return x=x>y?x:y;}
template<typename T>T cmin(T &x, T y){return x=x<y?x:y;}
template<typename T>
T &read(T &r){
	r=0;bool w=0;char ch=getchar();
	while(ch<'0'||ch>'9')w=ch=='-'?1:0,ch=getchar();
	while(ch>='0'&&ch<='9')r=r*10+(ch^48),ch=getchar();
	return r=w?-r:r;
}
template<typename T1,typename... T2>
void read(T1 &x,T2& ...y){read(x);read(y...);}
const int mod=998244353;
inline void cadd(int &x,int y){x=(x+y>=mod)?(x+y-mod):(x+y);}
inline void cdel(int &x,int y){x=(x-y<0)?(x-y+mod):(x-y);}
inline int add(int x,int y){return (x+y>=mod)?(x+y-mod):(x+y);}
inline int del(int x,int y){return (x-y<0)?(x-y+mod):(x-y);}
int qpow(int x,int y){
	int s=1;
	while(y){
		if(y&1)s=1ll*s*x%mod;
		x=1ll*x*x%mod;
		y>>=1;
	}
	return s;
}
const int N=500010;
struct Node{
	int ch[26],len,fa;
}a[N<<1];
int tot=1,las=1;
void Ins(int x){
	int cur=++tot;
	int p=las;a[cur].len=a[las].len+1;las=cur;
	for(;p&&!a[p].ch[x];p=a[p].fa)a[p].ch[x]=cur;
	if(!p)a[cur].fa=1;
	else{
		int q=a[p].ch[x];
		if(a[q].len==a[p].len+1)a[cur].fa=q;
		else{
			int c=++tot;a[c]=a[q];
			a[c].len=a[p].len+1;
			a[q].fa=a[cur].fa=c;
			for(;p&&a[p].ch[x]==q;p=a[p].fa)a[p].ch[x]=c;
		}
	}
}
int n;
char s[N];
int f[N],g[N<<1],ok[N<<1],po[N];
signed main(){
	#ifdef do_while_true
//		assert(freopen("data.in","r",stdin));
//		assert(freopen("data.out","w",stdout));
	#endif
	read(n);
	scanf("%s",s+1);
	reverse(s+1,s+n+1);
	for(int i=1;i<=n;i++)Ins(s[i]-'a');
	int lp=1,rp=1;
	int now=a[1].ch[s[1]-'a'];
	for(int i=1,k=1;i<=n;){
		po[i]=now;
		auto chk=[&](){
			return ok[lp]||ok[rp]||g[lp]>=k-1||g[rp]>=k-1;
		};
		function<void(int)>upd=[&](int x){
			if(x==0||ok[x])return ;
			ok[x]=1;upd(a[x].fa);
		};
		auto go=[&](int o){
			int x=po[o];
			cmax(g[x],f[o]);
			upd(a[x].fa);
		};
		while(k>1 && !chk()){
			--k;
			if(a[a[now].fa].len==k)now=a[now].fa;
			if(a[a[lp].fa].len==k-1)lp=a[lp].fa;
			if(a[a[rp].fa].len==k-1)rp=a[rp].fa;
			go(i-k);
		}
		f[i]=k;
		++i;++k;
		if(i>n)break;
		now=a[now].ch[s[i]-'a'];
		rp=a[rp].ch[s[i]-'a'];
		lp=a[lp].ch[s[i-1]-'a'];
	}
	int ans=0;
	for(int i=1;i<=n;i++)cmax(ans,f[i]);
	cout << ans << '\n';
    #ifdef do_while_true
//		cerr<<'\n'<<"Time:"<<1.0*clock()/CLOCKS_PER_SEC*1000<<" ms"<<'\n';
	#endif
	return 0;
}

标签:typedef,ch,String,int,题解,len,fa,Journey,include
From: https://www.cnblogs.com/do-while-true/p/17650711.html

相关文章

  • 视频集中存储平台EasyCVR视频融合平台接入RTSP设备出现离线情况的问题解决方案
    安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力,也具备接入AI智能分析的......
  • JS中的JSON.Stringify 方法详解
    JSON.stringify是JavaScript中的一个方法,用于将JavaScript对象转换为JSON字符串。语法:JSON.stringify(value,replacer,space)参数说明:value:要转换为JSON字符串的值。replacer(可选):用于过滤和转换结果的函数或数组。如果是函数,则只转换函数返回的结果;如果是数组,则......
  • CF757G 题解
    Lnk。这是一个dfs序+主席树的乱搞做法。首先把树上距离拆开,令\(\operatorname{dis}(u)\)表示\(u\)到根的路径长度:\[\left(\sum_{i=l}^r\operatorname{dis}(p_i)\right)+\left(\sum_{i=l}^r\operatorname{dis}(x)\right)-2\sum_{i=l}^r\operatorname{dis}(\operatorna......
  • [CEOI2011] Matching 题解
    [CEOI2011]Matching题解题外话:看了其他人题解后作为初学$kmp$的我非常蒙,因为对这个算法的核心掌握不太好,不知道怎么维护动态的序列,因此写下此题解共享经验,建议只会打模板的看看。参考资料:https://www.cnblogs.com/fusiwei/p/11944975.html思路引导:看到数据范围,又和真实......
  • 【题解】洛谷 P1002 [NOIP2002 普及组] 过河卒
    原题链接解题思路这是一道经典的动态规划题目。如果尝试使用深度优先搜索(dfs)或广度优先搜索(bfs)做就会获得TLE(注意数据范围)。于是我们想到了更为高级的动态规划(DynamicProgramming,dp)。简略介绍动态规划算法的核心思想:把原问题分解为相对简单的子问题的方式求解复杂问题。......
  • P8772 [蓝桥杯 2022 省 A] 求和 题解
    蒟蒻第一次发题解qwq$S=a_1\timesa_2+a_1\timesa_3+a1\timesa_n+a_2\timesa_3+···+a_n-2\timesa_n-1+a_n-1\timesa_n$从样例来看41369这道题就是要求$1\times3+1\times6+1\times9+3\times6+3\times9+6\times9$我们可以把这个算式分成几个部分$(1\t......
  • ABC314EX 题解
    模拟退火的题解。题目的难点在于如何计算点到所有线段的距离。我们可以通过求线段的直线解析式,然后计算与其垂直的直线的斜率,将随机出来的点带入求得直线解析式,然后求两直线交点,判断是否在线段上进行分讨即可,但是我可能写挂了,所以改成用的向量。我们看到有三种情况,我们可以分......
  • YACS 2023年8月月赛 甲组 T3 金字塔分割 题解
    看到这题,自然的想到DP啦!如果设$f_{i,j}$为到第$i$个位置前面的都合法且最后一段和为$j$是否可行,那么转移十分显然,但是状态会炸。此时我们考虑在状态上进行优化来减少时间,把$f_i$设为到第$i$个位置分段数量最多的情况下且最后一段和最少的和,以及能分成几段就好了。......
  • [USACO JAN 2011]交通灯 题解
    题意很清晰,直接跑SPFA求最短路。只是我们在松弛操作时,需要注意从\(u\)是否可以到达\(v\)。怎么判断呢?请移步下面三个部分。Part1先解释一下,下面点\(i\)的信息分别为以下变量:color表示颜色,1表示蓝色,0表示紫色num表示初始状态持续时间t1表示蓝色状态持续时间......
  • CF1818 & 1817 题解
    Div2A容易发现最后要存活下来一定要每次和\(1\)号做出相同的选择,直接数就好了.Div2B容易发现当\(n\)为奇数的时候无解。考虑\(n\)为偶数的情况怎么构造,有一种方案是在\(a_i=i\)的基础上调整,交换一下\(a_{2i-1}\)和\(a_{2i}\),证明考虑左右端点的奇偶性。Div2C&......