214最短迴文串(KMP算法——困難)

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章