「题解」Codeforces 1063F String Journey

「题解」Codeforces 1063F String Journey

先 reverse 一下。

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

然后考虑一手 dp,设 fi 表示最后一个子串是 i 为结尾,最长长度是多少。

这样转移就是 fifj+1,iff s[jfj+1,j] is s[ifj,i]'s substring

掏出了一个结论,fi+1fi+1,因为不成立的话 fi 一定可以调整得更大。

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

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

定位出这个节点之后,考虑需要判断是否存在上一个选出的串的右端点 j,其合法要满足 fjk1jikts[jfj+1,j] 的后缀.

注意到每次 i,k 变化时是同时 +1 或者 kk1,那么 ik 是不降的。于是扫 i,k 的时候再每次把 fik 给 SAM 上 s[ikfik+1,ik] 所代表的节点单点 chkmax,查询的时候就查 parent 子树 max,就可以了。这样总的时间复杂度是 O(nlogn)

真的有必要这样吗?

仔细想一想,由于子树中的等价类 len 一定大于当前节点的 len,所以子树中一旦出现了一个单点 chkmax,那么自己肯定是合法的,所以只需要记录出每个点子树中(不包含自己)是否已经出现过点,记作 ok,并且记录每个点处的 fmax,判断合法时直接判 fk 或者 ok 已经是否已经被标记过即可。扫 i,k 的时候加入 fik 时,暴力不断在 parent 树上跳 father 更新 ok 即可,如果之前已经跳到过了就不再跳了(一个点被标记那么其祖先已均被标记)。这样均摊下来总的时间复杂度是 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;
}
posted @   do_while_true  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 33 minutes 23 seconds

点击右上角即可分享
微信分享提示