欢迎访问LeetCode题解合集
题目描述
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
示例 1:
输入:s = "aacecaaa"
输出:"aaacecaaa"
示例 2:
输入:s = "abcd"
输出:"dcbabcd"
提示:
0 <= s.length <= 5 * 10^4
s
仅由小写英文字母组成
题解:
在 s
前面添加字符,将其变成回文串,本质就是求 s
的最长回文前缀。
比如 s = abcd
,其最长回文前缀为 a
,将剩下的部分复制一份,即可得到 ret = dcbabcd
。
法一:
Manacher算法。
考虑 manacher
算法,其可线性求每个位置的最长回文子串,关于 manacher
算法,请看 最长回文子串 。
从右到左计算回文半径时,关注回文半径最左即将到达的位置,一旦发现已经到达字符串起始字符前一个位置,说明必须包含第一个字符的最长回文半径已经找到,直接退出即可。
时间复杂度:$O(n)$
额外空间复杂度:$O(n)$
class Solution {
public:
string shortestPalindrome(string s) {
string t = "";
int len = s.size() * 2 + 1;
for ( int i = 0, k = 0; i < len; ++i ) {
if ( i & 1 ) t += s[k++];
else t += '#';
}
vector<int> p(len);
int r = len, c = len, ret = 0;
for ( int i = len - 1; i >= 0; --i ) {
p[i] = r < i ? min( i - r, p[2 * c - i] ) : 1;
while ( i + p[i] < len && i >= p[i] ) {
if ( t[i + p[i]] == t[i - p[i]] )
++p[i];
else break;
}
if ( i - p[i] < r ) {
r = i - p[i];
c = i;
}
if ( r == -1 ) {
ret = p[i] - 1;
break;
}
}
t = "";
for ( int i = s.size() - 1; i >= ret; --i )
t += s[i];
t += s;
return t;
}
};
/*
时间:4ms,击败:84.78%
内存:7.6MB,击败:52.17%
*/
法二:
KMP算法。
因为 kmp
算法中的 next[i]
表示以 i
为终点的后缀和从 1
开始的前缀相等,且后缀长度最长。
于是可以把 s
逆序 s'
,然后对 s + s'
求 next
数组。注意,我们可以在 s
和 s'
之间添加一个分隔符 #
,确保求得的前后缀不会超过 s
本身的长度。
时间复杂度:$O(n)$
额外空间复杂度:$O(n)$
class Solution {
public:
string shortestPalindrome(string s) {
string t( s.rbegin(), s.rend() );
string p(' ' + s + '#' + t);
int n = p.size() - 1;
vector<int> next(n + 1);
for ( int i = 2, j = 0; i <= n; ++i ) {
while ( j && p[i] != p[j + 1] ) j = next[j];
if ( p[i] == p[j + 1] ) ++j;
next[i] = j;
}
return t.substr(0, s.size() - next[n]) + s;
}
};
/*
时间:0ms,击败:100.00%
内存:7.8MB,击败:35.57%
*/