首页 > 其他分享 >【CF30E】Tricky and Clever Password 题解(manacher + exKMP)

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

时间:2023-12-26 19:11:57浏览次数:40  
标签:Tricky exKMP pt int 题解 mid len ans mx

manacher + exKMP + 二分。

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

Solution

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

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

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

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

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

  • 总复杂度 \(O(n\log n)\),瓶颈在二分,跑了 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.

标签:Tricky,exKMP,pt,int,题解,mid,len,ans,mx
From: https://www.cnblogs.com/gsn531/p/17929114.html

相关文章

  • [SNOI2019] 网络 题解
    [SNOI2019]网络题解最喜欢这道题。简要题意给一颗\(n\)个节点的树和一个参数\(d\),定义两个节点\(x,y\)之间的距离为\(x\)到\(y\)的简单路径上的边数。定义一个树上连通块的权值为连通块中任意两点的距离之和。定义一个树上连通块的直径为连通块中任意两点距离的最......
  • CF1887C Minimum Array 题解
    Problem-1887C-CodeforcesMinimumArray-洛谷有点被降智了/ll首先区间修改显然先转化成差分序列单点修改。显然对于相同的操作序列,\(a_i\)的取值对答案无影响,因此我们可以先让\(a_i\)全部取\(0\),最后再加回来即可假如说操作到某一时刻,\(a_i\)的值中第一个......
  • 洛谷B3647 【模板】Floyd 题解 floyd算法 求 多源多汇最短路
    题目链接:https://www.luogu.com.cn/problem/B3647floyd算法:https://oi-wiki.org/graph/shortest-path/#floyd-算法示例程序:#include<bits/stdc++.h>usingnamespacestd;constintmaxn=101;intn,m,f[maxn][maxn];intmain(){scanf("%d%d",&n......
  • [题解]CF1811D Umka and a Long Flight
    思路假设原题目中的\(n\)在本文中为\(num\),则原长方形的长\(m=f_{num+1}\)和宽\(n=f_{num}\)。显然对于最初始的长方形,显然是要将一个\(f_{num}\timesf_{num}\)的长方形丢进去的,并且要么放最左边,要么放在最右边。因为对于当前的\(m=f_{num+1}=f_{num}+......
  • CF768G The Winds of Winter题解
    我们考虑暴力咋做,每次得到一个森林之后,必定是从最大的树上摘一棵子树,挪到最小的树上,所以此时的答案为\(max(siz_{mx}-x,siz_{mn}+x,siz_{次大值})\),于是发现\(x=\frac{siz_{mx}-siz_{mn}}{2}\)时答案最优,所以只需找到这个值的前驱后继即可我们使用\(\text{multiset}\)实现,......
  • HNOI2017影魔题解
    HNOI2017影魔对于两种贡献,都只用考虑左边第一个比自己大的,和右边第一个比自己大的数,分别记为\(l_i、r_i\)对于询问一,每个数对\((l_i,r_i)\)构成全部情况对于询问二,可以拆分成\(x=l_i\)时,\(y\in[i+1,r_i-1]\),以及\(y=r_i\)时,\(x\in[l_i+1,i-1]\)我们考虑用扫描线......
  • 软件测试/测试开发|selenium NoSuchDriverException问题解决
    前言我们在使用selenium进行web自动化测试时,有时候会遇到NoSuchDriverException的问题,这个异常通常是由于WebDriver无法找到指定的浏览器驱动而引起的。在这篇文章中,我们将讨论NoSuchDriverException的原因以及如何解决这个问题。NoSuchDriverException是什么?NoSuchDriverExce......
  • CF1051C Vasya and Big Integers 题解
    Problem-1051E-CodeforcesVasyaandBigIntegers-洛谷感谢女队提交记录推荐给我的一道题\(Orz\)首先\(O(n^2)\)的\(dp\)是simple的,如果你没看出来你可能是像我一样把题目看错了设\(dp_i\)表示考虑前\(i\)个的方案数,转移枚举长度后比较字典序。虽然看......
  • P6922 [ICPC2016 WF] Longest Rivers 题解
    Description有\(n\)条河和\(m+1\)个交汇处构成一棵以\(0\)号点(即大海)为根的树。每条河有各自的名称。对于一个交汇处,从它流出的干流的名称是流入这个交汇处的各个支流的名称之一。一条河流的长度是以它为名称的河流的长度之和。对于一个可能的命名方案,一条河流的排名等于......
  • SOJ1972 题解
    题意设\(S\)为一个可重数集,满足所有元素均为非负整数。你可以对\(S\)进行若干次(可以为\(0\)次)如下操作:选择一个非负整数\(x\)满足\(x\)至少在\(S\)中出现了\(2\)次,在\(S\)中删除一个\(x\),并将\((x-1)\)或\((x+1)\)插入\(S\)。如果你选择插入\((x-1)\),你必......