【题解】[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;
}
相关推荐
Frank_refuel2 小时前
C++11之一场名为“搬家”的 C++ 之旅
开发语言·c++
hetao17338374 小时前
2026-10-03~04 hetao1733837 的刷题记录
c++·算法
Zootopia6265 小时前
飞行力学知识梳理1|飞行性能与稳定性
人工智能·python·算法·机器学习·无人机·学习方法·信息与通信
longlongzihan5 小时前
从LeetCode 70. 爬楼梯看动态规划:如何精准推导状态转移方程?
c++·算法·leetcode·动态规划
2601_960356386 小时前
2027校招采购岗解析:数学基础如何迁移到需求预测、成本与供应商分析
人工智能·算法
h_a_o777oah7 小时前
【树论】树链剖分:将树形结构转化为线性结构以处理树上路径问题(洛谷 P3384 附C++代码模板)
数据结构·算法·线段树·acm·树链剖分·树论·重链剖分
HEJOO97 小时前
指针的算术运算详解
java·数据结构·算法
Coder-magician7 小时前
《代码随想录》刷题打卡day47:并查集-part01
数据结构·算法·图论
垆边人似月.7 小时前
VLAN 资源池(100~200 分档)
数据结构·算法
潼心1412o8 小时前
C++初阶(长期更新)第6讲:类和对象(下)
开发语言·c++·算法