原题链接:https://leetcode-cn.com/problems/shortest-palindrome/
很显然的,这道题可以转化为求最长前缀回文子串的问题。得到该子串后,只需要将字符串s的剩余部分翻转并拼接到s的最前面即可得最短回文串。
接下来我们尝试用两种方法来解决这个问题。
方法1:KMP算法
设s中的最长前缀回文子串为s1,将s进行翻转得到s’,则s1’为s’的最长后缀回文子串。由回文串的性质可知s1 = s1’。
将s作为模式串,s’作为目标串进行匹配。当遍历到s’的末尾时,如果匹配到s中的第i个字符,说明s的最长前缀回文子串的长度为i。
KMP算法详解
https://blog.csdn.net/qq_37969433/article/details/82947411
AC代码如下
class Solution {
public:
string shortestPalindrome(string s) {
int len = s.size();
int next[len + 1];
int i = 0, j = -1;
next[0] = -1;
while(i < len)
{
if(j == -1 || s[i] == s[j])
next[++i] = ++j;
else
j = next[j];
}
string str = s;
reverse(s.begin(), s.end());
i = 0, j = 0;
while(j < len)
{
if(str[i] == s[j])
{
i++;
j++;
}
else
{
if(next[i] == -1)
{
i = 0;
j++;
}
else
{
i = next[i];
}
}
}
return s.substr(0, len - i) + str;
}
};
方法2:Manacher算法
Manacher是用于求最长回文子串的一种高效的算法,时间复杂度为O(n)。
Manacher算法的模板中加入一条判断语句(判断回文子串的左端点是否为字符串s的左端点这一条件)即可解决该题。
AC代码如下
class Solution {
public:
string shortestPalindrome(string s) {
int len = s.size();
char tmp[2*len+4];
int Len[2*len+4];
tmp[0] = '@';
for(int i = 1; i <= 2*len; i+=2)
{
tmp[i] = '#';
tmp[i+1] = s[i/2];
}
tmp[2*len+1] = '#';
tmp[2*len+2] = '$';
int mx = 0, ans = 0, po = 0;
for(int i = 1; i <= 2*len+1; i++)
{
if(i < mx)
{
Len[i] = min(mx-i, Len[2*po-i]);
}
else
{
Len[i] = 1;
}
while(tmp[i-Len[i]] == tmp[i+Len[i]])
{
Len[i]++;
}
if(Len[i] + i > mx)
{
mx = Len[i]+i;
po = i;
}
if(i == Len[i] && Len[i] - 1 > ans)
{
ans = Len[i] - 1;
}
}
string str = s;
reverse(s.begin(), s.end());
return s.substr(0, len-ans) + str;
}
};