前言
Manacher 算法,也称为马拉车算法,由美国计算机科学家 Glenn K. Manacher 在 1975 年提出,主要用于解决在给定字符串里查找最长回文子串的问题。相较于普通的暴力解法(时间复杂度为
O(n^2),Manacher 算法把时间复杂度优化到了O(n),效率显著提升。
一、模板
// 函数用于计算奇数和偶数长度回文子串的半径
vector<int> d1 ,d2;
string s;
void manacher() {
int n = s.size();
d1.resize(n);
d2.resize(n);
// 计算奇数长度回文子串半径 边界:(i-d1[i] , i+d1[i])
for (int i = 0, l = 0, r = -1; i < n; ++i) {
int k = (i > r) ? 1 : min(d1[l + r - i], r - i + 1);
while (i - k >= 0 && i + k < n && s[i - k] == s[i + k]) ++k;
d1[i] = --k;
if (i + k > r) l = i - k, r = i + k;
}
// 计算偶数长度回文子串半径 d[i]为中心两个的右边那个,所以d2[0]永远=-1 边界:(i-1-d1[i] , i+d1[i])
for (int i = 0, l = 0, r = -1; i < n; ++i) {
int k = (i > r) ? 0 : min(d2[l + r - i + 1], r - i + 1);
while (i - k - 1 >= 0 && i + k < n && s[i - k - 1] == s[i + k]) ++k;
d2[i] = --k;
if (i + k > r) l = i - k - 1, r = i + k;
}
}//max(max(d1)*2+1 , max(d2)*2+2)可得最长回文子串
二、题目尝试
算法选择讲解:
1、传统的算法寻找最长回文子字符串,需要遍历每个字符,再基于单个字符向两边扩张,扩张时获取的数据信息再换下一个字符做扩张操作时不可用,造成资源浪费。
2、相比于同等时间复杂度和空间复杂度的字符串哈希求最大回文字符串的代码,代码实现难度较高
3、manacher的代码实现会方便很多
1.https://www.luogu.com.cn/problem/P3805
这是一道纯模板题目,模板自己看一下,然后用这题试试,模板的使用看看有没有问题
2.https://www.luogu.com.cn/problem/P9606
模板理解了,就可以试试这道,模板的d1数组和d2数组是重点,里面存储的数据要看懂
#include<bits/stdc++.h>
using namespace std;
// 函数用于计算奇数和偶数长度回文子串的半径
vector<int> d1 , d2;
string s;
void manacher() {
int n = s.size();
d1.resize(n);
d2.resize(n);
// 计算奇数长度回文子串半径 边界:(i-d1[i] , i+d1[i])
for (int i = 0, l = 0, r = -1; i < n; ++i) {
int k = (i > r) ? 1 : min(d1[l + r - i], r - i + 1);
while (i - k >= 0 && i + k < n && s[i - k] == s[i + k]) ++k;
d1[i] = --k;
if (i + k > r) l = i - k, r = i + k;
}
// 计算偶数长度回文子串半径 d[i]为中心两个的右边那个,所以d2[0]永远=-1 边界:(i-1-d1[i] , i+d1[i])
for (int i = 0, l = 0, r = -1; i < n; ++i) {
int k = (i > r) ? 0 : min(d2[l + r - i + 1], r - i + 1);
while (i - k - 1 >= 0 && i + k < n && s[i - k - 1] == s[i + k]) ++k;
d2[i] = --k;
if (i + k > r) l = i - k - 1, r = i + k;
}
}//max(max(d1)*2+1 , max(d2)*2+2)可得最长回文子串
int main() {
int t;
cin >> t >> s;
manacher();
int mmax1 = -1 , mmax2 = -1;
for (int i = 0; i < (int)d1.size(); i++) {
if (i + d1[i] == (int)d1.size() - 1 && d1[i] > mmax1) mmax1 = d1[i];
if (i + d2[i] == (int)d2.size() - 1 && d2[i] > mmax2) mmax2 = d2[i];
}
if (mmax2 >= mmax1) {
cout << t - (mmax2 * 2 + 2);
return 0;
} else {
cout << t - (mmax1 * 2 + 1);
return 0;
}
return 0;
}