Manacher 算法解决最大回文字串
4 min
Manacher 算法是一种线性时间复杂度的算法,专门用于解决最长回文子串问题。它利用了回文串的对称性,以及已知的回文子串信息,来避免重复计算。
解决步骤
- 预处理: 在字符串的每个字符之间,以及字符串的首尾插入特殊字符(例如
#),这样可以将奇数长度和偶数长度的回文串都统一处理为奇数长度。 - 构建
p数组:p[i]表示以string[i]为中心的最长回文半径(包括string[i]本身)。 - 利用对称性:
- 维护一个
center和right,表示当前已知的最长回文子串的中心和右边界。 - 对于每个
i,如果i < right,则可以利用对称性,得到p[i]的一个初始值:p[i] = min(p[2 * center - i], right - i),然后再使用中心拓展法;如果i >= right,就直接在i处使用中心拓展法。 - 然后进行中心拓展法的运算,从
p[i]的初始值开始,向两边扩展,更新p[i]。 - 如果
i + p[i] > right,则更新center为i,right为i + p[i] - 1。 - 最后,对下一个
i重复以上步骤。
- 维护一个
- 找到最大值: 遍历
p数组,找到最大值,即可得到最长回文子串的长度和中心位置。 - 还原原字符串: 根据最长回文子串的中心位置和长度,还原原字符串中的最长回文子串。
代码实现(C++)
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>
std::string preprocess_string(std::string original_string) {
std::string preprocessed_string = "#";
for (char c: original_string) {
preprocessed_string += c;
preprocessed_string += "#";
}
return preprocessed_string;
}
int main() {
int T;
std::cin >> T;
while (T--) {
std::string original_string;
std::cin >> original_string;
std::string preprocessed_string = preprocess_string(original_string);
int64_t length = preprocessed_string.length();
int64_t center = 1, right = 1;
std::vector < int64_t > p(length);
p[0] = 0;
p[length - 1] = 0; // Manacher
for (int64_t i = 1; i < length - 2; i++) {
if (i < right) { // Find mirror i and p value
int64_t i_mirrored = 2 * center - i;
int64_t p_mirrored = p[i_mirrored];
if (i + p_mirrored - 1 <= right) {
p[i] = p_mirrored;
while ((i + p[i] - 1) < length - 1 && (i - p[i] + 1) > 0) {
if (p[i + p[i]] == p[i - p[i]]) {
p[i]++;
} else {
break;
}
}
} else {
p[i] = right - i + 1;
while ((i + p[i] - 1) < length - 1 && (i - p[i] + 1) > 0) {
if (p[i + p[i]] == p[i - p[i]]) {
p[i]++;
} else {
break;
}
}
}
} else {
p[i] = 1;
while ((i + p[i] - 1) < length - 1 && (i - p[i] + 1) > 0) {
if (preprocessed_string[i + p[i]] == preprocessed_string[i - p[i]]) {
p[i]++;
} else {
break;
}
}
}
if (i + p[i] - 1 > right) {
center = i;
right = i + p[i] - 1;
}
}
int64_t max_p = 0;
int64_t center_index = 0;
for (int64_t i = 0; i < length - 1; i++) {
if (p[i] > max_p) {
max_p = p[i];
center_index = i;
}
} // Translate to original string.
int64_t start_index = (center_index + 1 - max_p) / 2;
int64_t sub_string_length = max_p - 1;
std::cout << original_string.substr(start_index, sub_string_length) << std::endl;
}
}复杂度分析
- 时间复杂度: O(n)。
- 空间复杂度: O(n)。