【CF30E】Tricky and Clever Password 题解(manacher + exKMP)

manacher + exKMP + 二分。

感觉是最粗暴的方法,想出来之后自己硬莽了 4k,荣获题解区最长。

Solution

约定:下文所提及到的所有的回文串,均指奇长度回文串。

  • 显然把题目拆成两个部分,中间的回文串,以及两边相同的连续子串。考虑一下从哪个入手比较好。

  • 忘记是咋想的了,易得从两边相同连续子串 ac 会方便些。发现,它相当于是原串 S 和它的反串 S 做一次 exKMP,对于原串的每个位置 izi 表示从这一位开始最长的合法的 a 的长度。

  • 现在考虑对于确定的 a,c,如何找到最长的 b。我们发现,如果区间 [i+zi,nzi]n 是最后一个下标)中最长的回文串 b 如果覆盖了整个区间,那么这就是 k=1 的情况,即 [i,n] 是一整个回文串。所以,对于 k=3 的情况,b 不可能覆盖完整个 [i+zi,nzi] 区间,所以直接二分答案找到区间 [i+zi,nzi] 中的最长回文串长度即可。

  • 考虑如何找到任意一个区间中最长回文串的长度。方法比较套路,二分一个长度 mid,表示查找区间中是否存在 2×mid+1 的回文串。check(mid) 就是询问区间是否存在 i[l+mid,rmid],满足 pi2×mid+1,其中 pi 表示以 i 为中心的最长回文串长度,manacher 预处理就好。

  • 总复杂度 O(nlogn),瓶颈在二分,跑了 122 ms,感觉挺不错的了。

Code

实现的话,细节就很多了。

manacher 和 exKMP 的部分直接套板子就行,注意一下 manacher 是把原串扩充之后做的,但做完得转回原串。

存答案的时候也挺麻烦的。

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define per(i, a, b) for(int i = a; i >= b; --i)
const int maxn = 2e5 + 5;
int n, m;
char a[maxn], b[maxn], s[maxn << 1];
int q[maxn << 1], p[maxn];
int nxt[maxn], z[maxn];
int st[maxn][30], lg[maxn];
int ans, typ, pt[5][5];

inline void input(){
	char ch = getchar();
	s[0] = '~', s[m = 1] = '#';
	while(ch < 'a' or ch > 'z') ch = getchar();
	while(ch >= 'a' and ch <= 'z') 
		s[++m] = ch, s[++m] = '#', ch = getchar();
}
inline void manacher(){
	int mx = 0, pos = 0;
	rep(i, 1, m){
		q[i] = mx > i ? min(mx - i + 1, q[pos * 2 - i]) : 1;
		while(s[i + q[i]] == s[i - q[i]]) q[i] += 1;
		if(i + q[i] - 1 >= mx)
			mx = i + q[i] - 1, pos = i;
	}
	int tp = -1;
	rep(i, 1, m) if(s[i] >= 'a' and s[i] <= 'z') p[++tp] = (q[i] / 2 - 1) * 2 + 1;
}
inline void getnxt(){
	nxt[0] = n;
	int mx = 0, pos = 1;
	while(nxt[1] + 1 < n and b[nxt[1]] == b[nxt[1] + 1]) nxt[1] += 1; 
	mx = nxt[1];
	rep(i, 2, n - 1){
		nxt[i] = mx < i ? 0 : min(nxt[i - pos], mx - i + 1);
		while(i + nxt[i] < n and b[i + nxt[i]] == b[nxt[i]]) nxt[i] += 1;
		if(i + nxt[i] - 1 >= mx) 
			mx = i + nxt[i] - 1, pos = i;
	}
}
inline void getz(){
	int mx = 0, pos = 0;
	while(z[0] < n and b[z[0]] == a[z[0]]) z[0] += 1;
	mx = z[0] - 1;
	rep(i, 1, n - 1){
		z[i] = mx < i ? 0 : min(nxt[i - pos], mx - i + 1);
		while(i + z[i] < n and a[i + z[i]] == b[z[i]]) z[i] += 1;
		if(i + z[i] - 1 >= mx)
			mx = i + z[i] - 1, pos = i;
	}
}
inline int Max(int x, int y){ return p[x] > p[y] ? x : y;}
inline void pre_st(){
	lg[0] = -1; rep(i, 1, 2e5) lg[i] = lg[i / 2] + 1;
	rep(i, 0, n - 1) st[i][0] = i;
	for(int j = 1; (1 << j) <= n; ++j)
		for(int i = 0; (i + (1 << j) - 1) < n; ++i)
			st[i][j] = Max(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
}
inline int qry_st(int l, int r){
	if(l == r) return l;
	if(l > r) return 0;
	int k = lg[r - l + 1];
	return Max(st[l][k], st[r - (1 << k) + 1][k]);
}
int id;
inline int qry_mxlen(int L, int R){
	int l = 1, r = (R - L) / 2 + 1, ans = 1; id = -L;
	while(l <= r){
		int mid = (l + r) >> 1, tmp;
		if(p[tmp = qry_st(L + mid, R - mid)] >= mid * 2 + 1) 
			ans = mid, id = tmp, l = mid + 1;
		else r = mid - 1;
	} 
	if(id == -L) ans = 1, id *= -1; else ans = ans * 2 + 1;
	return ans;
}

signed main(){
	input();
	n = -1;
	rep(i, 1, m) if(s[i] >= 'a' and s[i] <= 'z') a[++n] = s[i];
	rep(i, 0, n) b[n - i] = a[i];
	n += 1;
	manacher(), getnxt(), getz();
	pre_st();
	ans = 1, typ = 1, pt[1][0] = 0, pt[1][1] = 1;
	rep(i, 0, n - 1) if(p[i] > ans) 
		ans = p[i], pt[1][0] = i - (p[i] + 1) / 2 + 1, pt[1][1] = p[i];
	rep(i, 0, n - 1){
		if(i + z[i] >= n - 1 - z[i]){
			int len = (n - i); 
			if(len & 1){ if(ans >= len) continue;} else{ if(ans >= len - 1) continue;}
			if(len & 1){
				ans = len, typ = 1;
				pt[1][0] = i, pt[1][1] = len;
			} else{
				ans = len - 1, typ = 3;
				pt[1][0] = i, pt[1][1] = len / 2 - 1;
				pt[2][0] = i + pt[1][1], pt[2][1] = 1;
				pt[3][0] = pt[2][0] + 2, pt[3][1] = len / 2 - 1;
			}
			continue;
		}
		int len = qry_mxlen(i + z[i], n - 1 - z[i]), 
			fll = (n - 1 - z[i]) - (i + z[i]) + 1,
			op = (len == fll ? 1 : 3);
		if(ans >= len + z[i] * 2) continue;
		ans = len + z[i] * 2, typ = op;
		if(op == 1) pt[1][0] = i, pt[1][1] = ans;
		else{
			pt[1][0] = i, pt[1][1] = z[i];
			pt[2][0] = id - (len + 1) / 2 + 1, pt[2][1] = len;
			pt[3][0] = n - 1 - z[i] + 1, pt[3][1] = z[i];
		}
	}
	printf("%lld\n", typ);
	rep(i, 1, typ) printf("%lld %lld\n", pt[i][0] + 1, pt[i][1]);
	return 0;
}

Thanks for reading.

posted @   pldzy  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示