leetcode5 最长回文子串(Manacher算法)
1.题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
示例:
输入: “babad”
输出: “bab”
注意: “aba”也是一个有效答案。
2.manacher算法
奇回文(如 aba)和偶回文(如abba)归一化处理
- 在字符串s的前端添加 $** 字符防止越界(代码中有说明越界点),字符串s尾端已有 ‘\0’**
- 添加#字符于字符串s各字符之间
如abacddc
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
s[i] | a | b | a | c | d | d | c | \0 |
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
s_new[i] | $ | # | a | # | b | # | a | # | c | # | d | # | d | # | c | # |
* 参数设定
id 为当前最长回文串的中心
mx 为当前最长回文串的右边界
p[i] 为以i为中心的回文串半径
参考下图:
关键点(动态规划思想)
当 i 在当前最长回文子串内时( mx>i ),以 i 为中心的回文子串长度:p[i] = max > i ? min(p[2*id - i], mx-i) : 1;
- 要么等于其 关于id对称点j为中心的回文子串长度(前提:以j为中心的回文子串包含在当前最长回文子串内)
- 要么以其为中心的回文子串右端 超出mx ( 此时p[i]设为mx-i,且继续寻找边界 )
3.代码
string longestPalindrome(string s) {
int max_str = 0; // 最长回文子串的长度
int front_index; // 最长回文子串的起始位
if(s.empty())
return 0;
// 初始化s
string new_s;
new_s.push_back('$');
new_s.push_back('#');
for(int i = 0; i < s.length(); i++)
{
new_s.push_back(s[i]);
new_s.push_back('#');
}
vector<int> p(new_s.length());
int id = 0;
int mx = 0;
for(int i = 1; i < new_s.length(); i++)
{
// p[i]为当前i位置回文子串的半径,回文子串长度为p[i] - 1
// 加速方式(动态规划):p[i] = max > i ? min(p[2*id - i], mx-i) : 1;
if(mx > i)
p[i] = min(p[2*id - i], mx-i);
else
p[i] = 1;
// 前后搜索,越界点
while(new_s[i + p[i]] == new_s[i - p[i]])
p[i]++;
if(p[i] - 1 > max_str){
front_index = (i - p[i] + 2)/2 - 1; // 一定是 #......# 的形式
max_str = p[i] - 1;
}
if(p[i] + i > mx){
mx = p[i] + i;
id = i;
}
}
return s.substr(front_index, max_str);
}