原题目
代码分析
题目可理解为找最长回文前缀,将不满足的部分翻转添加在首部即可
如:
例1:aacecaaa
回文前缀为aacecaa 剩余a
为使其构成回文添加a
形成aaacecaaa
例2:abcd
最长前缀为a,剩余bcd
为使其构成回文添加dcb
形成dcbabcd
难点:最长回文前缀怎么找
方法一:暴力法
先翻转原字符串,找最长前缀回文串,就是找原字符串的前缀和翻转字符串的后缀能匹配的最大长度
例1:原 aacecaa a
翻转 a aacecaa
翻转字符串前剩余的添加到源字符串前面形成aaacecaaa
例2: 原 a bcd
翻转 dcb a
翻转字符串前剩余的添加到源字符串前面形成dcbabcd
比较时,不断缩小原字符串的前缀和翻转字符串的后缀,直到相等为止
class Solution {
public:
string shortestPalindrome(string s) {
int len = s.size();
string rev(s.rbegin(),s.rend());//翻转
string temp1 = s,temp2=rev;//temp1 s的前缀,temp2 rev的后缀
for(int i = 0; i < s.size(); i++){
if(temp1==temp2){
return rev.substr(0,i)+s;
}
temp1.pop_back();
temp2.erase(temp2.begin());
}
return "";
}
};
方法二:递归
找最长回文前缀
如果我们可以减小字符串的大小来搜索子字符串,而是不每次都检查完整的子字符串,就可以使该过程更加高效。
例如字符串“abcbabcaba”。 考虑两个指针 i 和 j。初始化 i = 0。jj 从 n−1 到 0 迭代,每当s[i]==s[j] 时,将 i 增加 1。现在,我们只需要 在[0,i) 范围内搜索。这样,我们就缩小了从开头寻找最大回文子串的搜索长度。[0,i) 必然包括最大回文子串。其正确性证明如下:若字符串全部为回文,则i会自增n次。若结尾有其他字符,i 会自增回文子串的长度。于是,即使 [0,i) 并不总是紧界,但它总包含从开头开始的最长回文子串。
简单来说就是不连续匹配前后字符,找到最长能匹配的长度,不断缩减,直到成为回文即可
例1:原 aacecaaa
翻转 aaacecaa
递归1:
aacecaaa
i = 6
返回 翻转最后不符合回文前缀的字符+递归前6个字符+最后不符合回文前缀的字符
递归2:
aacecaa
i = 6
返回当前回文字符串
结果a + aacecaa + a
class Solution {
public:
string shortestPalindrome(string s) {
int i = 0;
string rev(s.rbegin(),s.rend());
for(int j = s.size()-1; j>=0;j--){
if(s[i] == s[j]){
i++;
}
}
if(i == s.size()){
return s;
}
return rev.substr(0,s.size()-i)+shortestPalindrome(s.substr(0,i))+s.substr(i);
}
};
方法三:KMP
后缀找前缀的最大匹配字符,想到了KMP算法中的next函数
我们要找原字符串的前缀和翻转字符串的后缀能匹配的最大长度,可以将原字符串和翻转字符串合并,利用KMP找到最后一个字符能匹配到的最大前缀,因为前缀最大为原字符串长度,在中间加个#就不会找到比原字符串长的前缀了
例1:原 aacecaaa
翻转 aaacecaa
aacecaaa#a aacecaa
aacecaa a#aaacecaa
a | a | c | e | c | a | a | a | # | a | a | a | c | e | c | a | a | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 0 | 0 | 0 | 1 | 2 | 2 | 0 | 1 | 2 | 2 | 3 | 4 | 5 | 6 | 7 |
最长前缀匹配数为7,将翻转字符串剩余字符添加在原字符串首部
例2: 原 abcd
翻转 dcba
abcd#dcb a
a bcd#dcba
a | b | c | d | # | d | c | b | a | |
---|---|---|---|---|---|---|---|---|---|
-1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
最长前缀匹配数为1,将翻转字符串剩余字符添加在原字符串首部
class Solution {
public:
string shortestPalindrome(string s) {
//KMP
string rev(s.rbegin(),s.rend());
string str = s + "#" + rev;
vector<int>next(str.size()+1);
next[0] = -1;
int i = 0, j = -1;
while(i<str.size()){
if(j == -1||str[i] == str[j]){
i++;
j++;
next[i] = j;
}else{
j = next[j];
}
}
return rev.substr(0,s.size()-next[str.size()])+s;
}
};