题意为,允许在字符串的头部添加字符,使其成为回文。最简单的方法是讲该字符串翻转为s2, s2 + s1即为回文,但题目要求的是最短的回文串。
于是问题便转换为一个,求以原字符串首字符开头(s[0])的最长子串的长度(亦即使得s.substr(0, length)为回文的最大length值)这么一个子问题。
—— 可以想象,当求得这么一个length后,我们将s.substr(length)翻转添加到s头部即为所求(亦即s.substr(length).reverse() + s即为所求)
这个子问题可暴力求解O(N^2)的复杂度,也可以用下述的Manacher算法在O(N)时间内求解。
Manacher算法实现了在O(N)时间内求字符串的最长回文子串(LeetCode 5. Longest Palindromic Substring)
在了解了该算法后,要求以原字符串首字符开头(s[0])的最长子串的长度,只要添加约束p[i] == i, 表示以i为中心回文串,其一边的长度为p[i].
若p[i] == i, 那么从i往前推p[i]个位置,那就是原点 —— 所以这是一个包含原字符串s首字母s[0]的回文子串。
通过Manacher算法,求满足p[i] == i的最大i值即可。
代码:
class Solution
{
public:
string shortestPalindrome(string s)
{
string str(2*s.size()+1, 0);
for (size_t i = 1; i < str.size(); i += 2)
{
str[i] = s[i>>1];
}
vector<int> p(2*s.size()+1, 0);
int id=0, mx=0;
for (int i = 1; i < str.size(); ++ i)
{
p[i] = mx>i? min(p[2*id-i], mx-i): 0;
for (; i-p[i]-1>=0 && i+p[i]+1<str.size()
&& str[i-p[i]-1]==str[i+p[i]+1]; ++ p[i]) {}
if (i + p[i] > mx)
{
mx = i + p[i];
id = i;
}
}
int max_length = 0;
for (int i = 1; i < str.size(); ++ i)
{
max_length = i==p[i]? i: max_length;
}
string non_palindrome_postfix = s.substr(max_length);
reverse(non_palindrome_postfix.begin(), non_palindrome_postfix.end());
return non_palindrome_postfix + s;
}
};