【题解】[JSOI2016] 扭动的回文串

P4324 JSOI2016 扭动的回文串 - 洛谷 (luogu.com.cn)

想了很久 Manacher 解法没成功,一看题解可以用纯哈希过。


前两种情况无论你使用哈希还是马拉车,都可以很快解决,所以着重看第三种情况。

回文串相关的题目一定和枚举中心点相关,往这方面想。

如何在确定中心点 的情况下,让两个串的匹配最大化?

假设当前中心点在 A,如果和中心点连着的一段下标对应的字符 A 串和 B 串的一样,

我们优先选 A 的,反之优先选 B 的。

如何让这种"连着"最大化?那其实就是在找在 A 中以 i 为中心的最长回文半径。

以这个半径作为固定要选在 A 的字符,往左边向 A 拓展,往右边向 B 拓展,看是否能匹配。

这个匹配的过程可以现用现算,二分就行。

可以证明这样一定是最优解之一,中心点在 B 同理,偶数情况也不难。

由于这个做法一定带一个 log,所以求回文半径也不用马拉车了,一样用二分哈希。


怎么说呢?是一个比较巧妙的解法吧,第一次看有点想不到,但其实思路挺自然。

最主要是想到回文半径的基础上配对一定是最优解之一。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ULL;   // 自然溢出 
const int N = 1e5 + 10;
const ULL P = 13331;   // 哈希基数 

int n;
char sa[N], sb[N];
ULL ha[N], hb[N], rha[N], rhb[N];
ULL facp[N];

void init() {
	ha[0] = 0; hb[0] = 0;
	for (int i = 1; i <= n; i ++) {
		ha[i] = ha[i - 1] * P + (sa[i] - 'A' + 1);
		hb[i] = hb[i - 1] * P + (sb[i] - 'A' + 1);
	}
	rha[n + 1] = 0; rhb[n + 1] = 0;
	for (int i = n; i >= 1; i --) {
		rha[i] = rha[i + 1] * P + (sa[i] - 'A' + 1);
		rhb[i] = rhb[i + 1] * P + (sb[i] - 'A' + 1);
	}
	facp[0] = 1;
	for (int i = 1; i <= n + 1; i ++) {
		facp[i] = facp[i - 1] * P;
	}
}

ULL get_h(ULL h[], int l, int r) {
	return h[r] - h[l - 1] * facp[r - l + 1];
}

ULL get_rh(ULL rh[], int l, int r) {
	return rh[l] - rh[r + 1] * facp[r - l + 1];
}

int get_expand(ULL h[], ULL rh[], int ql, int qr) {
	int l = 0, r = min(ql, n - qr + 1), res = l;
	while (l <= r) {
		int mid = (l + r) >> 1;
		ULL ta = get_h(h, ql - mid + 1, ql);
		ULL tb = get_rh(rh, qr, qr + mid - 1);
		if (ta == tb) {
			l = mid + 1;
			res = mid;
		}
		else {
			r = mid - 1;
		}
	}
	return res;
} 

int main () {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> n;
	cin >> (sa + 1) >> (sb + 1);
	
	init();
	
	int ans = 1;
	
	for (int i = 2; i <= n; i ++) {
		int la = get_expand(ha, rha, i, i);
		int lb = get_expand(hb, rhb, i, i);
		ans = max(ans, la * 2 - 1 + get_expand(ha, rhb, i - la, i + la - 1) * 2);
		// 在 A 里 [i - la + 1, i + la - 1] 都是回文半径,B 的第一个和 A 的最后一个共用一个下标 
		ans = max(ans, lb * 2 - 1 + get_expand(ha, rhb, i - lb + 1, i + lb) * 2);
		// 在 B 里 [i - lb + 1, i + lb - 1] 都是回文半径,A 的最后一个和 B 的第一个共用一个下标 
	}
	
	for (int i = 1; i < n; i ++) {
		int la = get_expand(ha, rha, i, i + 1);
		int lb = get_expand(hb, rhb, i, i + 1);
		ans = max(ans, la * 2 + get_expand(ha, rhb, i - la, i + la) * 2);
		// 在 A 里 [i - la + 1, i + la] 都是回文半径,B 的第一个和 A 的最后一个共用一个下标 
		ans = max(ans, lb * 2 + get_expand(ha, rhb, i - lb + 1, i + lb + 1) * 2);
		// 在 B 里 [i - lb + 1, i + lb] 都是回文半径,A 的最后一个和 B 的第一个共用一个下标 
	}
	
	cout << ans << "\n";
	
	return 0;
}
相关推荐
DRobot10 分钟前
【机械制图与CAD实战(二十五)】螺纹的表示方法
算法·机械制图
用户老李26 分钟前
卡尔曼滤波自学教程 10:无迹卡尔曼滤波(UKF)
算法
深圳市益普科技有限公司2 小时前
半导体良率管理YMS:良率与设备参数的相关性,先拉哪几组数据
人工智能·python·算法
努力努力再努力wz2 小时前
【C++高阶系列】C++20 协程底层原理:函数为什么能暂停?从状态机到 Task,彻底串起挂起、恢复与协程调度
开发语言·c++·c++20·1024程序员节
程序员清风3 小时前
算法题中的边界条件、测试与调试
开发语言·算法·kotlin
划破黑暗的第一缕曙光3 小时前
[C++]:4.模板
c++·template·模板·函数模版·类模版
十五年专注C++开发3 小时前
pybind11:一个跨C++和Python平台的编程库
开发语言·c++·python·pybind11
CodeYp20193 小时前
第11讲:stack 和 queue
开发语言·c++·queue·stack
炘爚3 小时前
数据结构算法总结
数据结构·python·算法
Dageno_AI4 小时前
ChatGPT 不再引用 Reddit 之后,还值得在 Reddit 上做 AI 可见度吗?
算法