「JSOI2019」节日庆典 (Z-Algorithm)

传送门

  • 考虑一个后缀 S i . . . n S_{i...n} Si...n,如果加上一个任意字符 c c c 可以使得 S i . . . n c S_{i...n}c Si...nc 为字典序最小的后缀,那么将其称为好后缀,跟 Z J O I 2017 ZJOI2017 ZJOI2017 的结论一样,好后缀的集合大小是 log ⁡ ∣ S ∣ \log |S| logS 的,并且好后缀的长度每次至少翻倍,证明:ZJOI2017字符串
  • 于是暴力维护这个集合,只需要考虑比较大小,发现每次需要比较一个后缀和从 1 开始的后缀大小,用 Z − a l g o r i t h m Z-algorithm Zalgorithm 可以 O ( 1 ) O(1) O(1) L C P LCP LCP,复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs int N = 3e6 + 50;
int n; char S[N];
int main(){
	#ifdef FSYolanda
	freopen("1.in","r",stdin);
	#endif
	scanf("%s",S+1); n=strlen(S+1);
	static int lcp[N]; int mx=1, pt=1; lcp[1]=n;
	for(int i=2; i<=n; i++){
		if(i<=mx) lcp[i]=min(lcp[i-pt+1],mx-i+1);
		while(i+lcp[i]<=n && S[i+lcp[i]]==S[1+lcp[i]]) ++lcp[i];
		if(i+lcp[i]-1>mx) pt=i, mx=i+lcp[i]-1;
	} 
	vector<int> St;
	for(int i=1; i<=n; i++){
		St.pb(i); vector<int> nw;
		for(auto t : St){
			bool ok = true;
			while(!nw.empty()){
				int x = nw.back();
				if(S[i]>S[i-t+x]) ok = false;
				if(S[i]>=S[i-t+x]) break; nw.pop_back();
			} if(ok&&(nw.empty()||(i-t<=t-nw.back()))) nw.pb(t);
		} St = nw;
		int as = St[0];
		for(int j=1,t; j<St.size(); j++){
			t = St[j]; int pt = as + i - t + 1;
			bool ok = false;
			if(pt+lcp[pt]<=i) ok|=S[1+lcp[pt]]<S[pt+lcp[pt]];
			else{ 
				int x=1+i-pt+1;
				if(x+lcp[x]<=i) ok|=S[x+lcp[x]]<S[1+lcp[x]];
			} if(ok) as = t;
		} cout << as << " "; 
	} return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值