1、題目描述
給定一個字符串 s,你可以通過在字符串前面添加字符將其轉換爲迴文串。找到並返回可以用這種方式轉換的最短迴文串。
2、示例
輸入: "aacecaaa"
輸出: "aaacecaaa"
3、題解
問題可以簡化爲從s的開頭尋找最大的迴文子串,然後將剩下的後面部分翻轉後接到s的前面。例如對於字符串aacecaaba,從開頭找到的最大的迴文子串是aacecaa,剩下部分是ba翻轉得ab,拼接到s的前面得abaacecaaba。
基本思想:KMP算法,next[j]表示j之前的字符串的最長前綴和後綴相等的字符個數,即最長前綴的下一個字符下標。
對於s=aacecaaba翻轉後得s_reverse=abaacecaa那麼兩個字符串拼接s_reverse+s一定是迴文串,但不一定是最短的迴文串,要想得到最短的迴文串,只需要將s的前綴和s_reverse的後綴最大重複部分(即aacecaa)合併就得到最短迴文串。
這就聯想到KMP算法中的next數組,就是用來查找一個字符串的前綴和後綴相同的長度的最大值,因爲查找的是s的前綴和s_reverse的後綴相同的部分,所以s+s_reverse拼接得到新串,然後求得新串的next數組即可,但是我們所求的前綴是不能超過中點的,因此用一個特殊字符隔開。
最後將s_reverse與s非重複重複部分截取拼接上s就是最短迴文串結果。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
public:
string shortestPalindrome(string s) {
//基本思想:KMP算法,問題可以簡化爲從s的開頭尋找最大的迴文子串,然後將剩下的後面部分翻轉後接到s的前面。
//例如對於字符串aacecaaba,從開頭找到的最大的迴文子串是aacecaa,剩下部分是ba翻轉得ab,拼接到s的前面得abaacecaaba
//另一種思路:s=aacecaaba翻轉後得s_reverse=abaacecaa那麼兩個字符串拼接一定是迴文串,但不一定是最短的迴文串,
//要想得到最短的迴文串,只需要將s的前綴和s_reverse的後綴最大重複部分合並就得到最短迴文串
//這就聯想到KMP算法中的next數組,就是用來查找一個字符串的前綴和後綴相同的長度的最大值
//所以將s和s_reverse拼接得到新串,然後求得新串的next數組即可
//但是我們所求的前綴是不能超過中點的,因此用一個特殊字符隔開
string s_reverse(s), s_new;
reverse(s_reverse.begin(), s_reverse.end());
s_new = s + "#" + s_reverse;
int i = -1, j = 0; //i指向前綴下標初始-1,j指向後綴下標初始0
//next[j]表示j之前的字符串的最長前綴和後綴相等的字符個數,即最長前綴的下一個字符下標
vector<int> next(s_new.size() + 1, 0);
next[0] = -1;
//循環掃描字符串s_new
while (j < s_new.size())
{
if (i == -1 || s_new[i] == s_new[j])
{
i++;
j++;
next[j] = i;
}
else
i = next[i];
}
//將s_new的s_reverse的與s非重複重複部分截取拼接上s就是最短迴文串結果
return s_new.substr(s.size() + 1, s.size() - next[s_new.size()]) + s;
}
};
int main()
{
Solution solute;
string s = "aacecaaa";
cout << solute.shortestPalindrome(s) << endl;
return 0;
}