Given a string S, you are allowed to convert it to a palindrome by adding characters in front of it. Find and return the shortest palindrome you can find by performing this transformation.
For example:
Given "aacecaaa"
, return "aaacecaaa"
.
Given "abcd"
, return "dcbabcd"
.
Credits:
Special thanks to @ifanchu for adding this problem and creating all test cases. Thanks to @Freezen for additional test cases.
Subscribe to see which companies asked this question.
在字符串前面加字符,使得字符串变成回文字符串,要求加的字符尽量少,返回由源字符串变成的最短的回文字符串。这个问题可以转化为求字符串包含开头部分的最长的回文子字符串的长度,求得之后补剩下的字符即可。这里的实现用到了kmp算法的思想。
首先kmp算法的想法是对需要查找的字符串进行预处理,求出一张前缀匹配表(自己瞎命名的),对每个位置,记录下以其为结尾的某子字符串在字符串前缀重复的长度,这样如果某个位置匹配不成功,就不用返回到最开头的位置重新开始。比如"ABAB",对应的表示“0012”。对于某一个位置的计算方法是,根据前一个字符对应的前缀长度len,找出在前缀中相应的位置,判断当前位置的字符和前缀位置的字符(s[len],也就是前一个字符对应的前缀位置的下一个字符)是否相等,相等的话说明还是前缀,长度加长1保存起来;不然的话继续往前找。以一个例子说明,对于字符串"ABABABABC",前面四个对应的表是“0012”,对第五个字符‘A’,前面的‘B’对应的值是2,取得s[2]为‘A’,与s[4]相等,所以table[4]=table[1]+1=1;同理table[5]=table[2]+1=2,table[6]=table[3]+1=3,table[7]=table[4]+1=4,所以现在的表是“00121234”。到了第九个字符‘C’,首先前面的‘B’对应的值是4,而s[4]为‘A’,与‘C’不相等,继续往前找(因为前面可能有短一点的前缀),对于位置4的前一个位置3,table[3]为2,取得s[2]为‘A’,也是不相等;继续往前找,位置2的前一个位置1,table[1]为0,说明了已经没有适合的前缀了。所以令table[8]=0。
把kmp的想法应用在寻找最长的前缀回文子字符串。把原字符串倒转拼接在原字符串后面,以一个字符分隔开(随便!@¥#……¥%都行)。然后也是求一张前缀匹配表。最长的前缀回文子字符串长度就是表的最后一项。比如“aacecaaa”,拼接后“aacecaaa$aaacecaa”,对应的表是“01000121201234567”,最长的前缀回文子字符串长度就是7.
代码:
class Solution {
public:
string shortestPalindrome(string s)
{
if(s.empty()) return s;
string t = s;
reverse(t.begin(), t.end());
get_table(s + "$" + t);
//for(auto item:table) cout << item << endl;
return t.substr(0, s.size() - table.back()) + s;
}
private:
vector<int>table;
void get_table(string s)
{
table.resize(s.size(), 0);
for(int i = 1; i < s.size(); ++i)
{
int prev = i - 1;
while(prev >= 0 && s[i] != s[table[prev]])
{
prev = table[prev] - 1;
}
if(prev >= 0) table[i] = table[prev] + 1;
}
}
};