Manacher演算法,最長迴文串,時間複雜度O(n)
阿新 • • 發佈:2019-01-24
最長迴文子串 問題
對於一個字串,請設計一個高效演算法,計算其中最長迴文子串的長度。
給定字串A以及它的長度n,請返回最長迴文子串的長度。
測試樣例:
“abc1234321ab”,12
返回:7
中心擴充套件到Manacher
中心擴充套件時 奇數和偶數的情況是不一樣的,需要分開處理
採用Manacher 演算法,對字串每隔一個位置加上一個特殊字元(特殊字元隨便是什麼字元),這樣就轉化成為奇數求解了
例如
“121” 變成 “#1#2#1#”
“1221” 變成 “#1#2#2#1#”
接下來將逐步求解
所需變數
p[] int型陣列
* p[i] 表示 以i位置為中心的迴文半徑長(其長度減去1,表示在原字串中以i為中心的迴文長度)
pR int型變數
* 表示 迴文半徑最長的位置的下一個位置(pR總是比上一個pR值要大的)
index 型變數
* 表示 迴文中心點,pR更新時index要跟著更新,否則index不用更新
初始情況
當i = 0時, p[0] = 1, index = 0, pR =1
到i = 1時 p[1] = 2 , index = 1, pR = 3
接著可以從i=2開始計算
共4種情況(如下的1,2,3,4)
說明
* 左大為關於某個點最大回文串的左位置
* 右大為關於某個點最大回文串的左位置
* i為當前遍歷的位置
* i’為i關於index的對稱位置
1. i關於index對稱點i’在index的左大的左側時
i關於index對稱點i’在index的左大的右側時 有分為三種情況
2. i’的左大 大於 index的左大
例項如下
3. i’的左大 小於 index的左大
4. i’的左大 等於 index的左大
例項如下
ac程式碼,按照上文思路即可
class Palindrome {
public:
int getLongestPalindrome(string A, int n) {
// write code here
if (n <= 0)
return 0;
string str;//新增n+1個特殊字元',迴文串都會變成奇數
for (int i = 0; i < n; i++)
{
str += "#";
str += A[i];
}
str += "#";
int len = n + n + 1;
vector<int> p(len); // p[i]為迴文中心半徑陣列(包括以str[i])
int index; // 迴文中心位置, pR更新時 index跟著跟新; pR不更新,index保持不變
int pR; // 迴文中心最右邊的 下一個
int maxlen = 1; //最大回文長度變數
// i=0, i=1單獨處理
p[0] = 1;
p[1] = 2;
index = 1;
pR = 3;
// 從 p[2] 開始掃描
for (int i = 2; i < len; i++)
{
int _i = index - (i - index); //i的對稱位置i'
int index_left = index - p[index] + 1; // index的左大
int index_right = pR -1; // index的右大
int _i_left = _i -p[_i] + 1; //i的對稱位置i'的左大
if (_i < index_left)
{
int j = i + 1;
int _j = i - 1;
while (str[_j] == str[j] && _j >= 0 && j < len)
{
_j--;
j++;
if (_j < 0 || j >= len)
break;
}
p[i] = j - i;
pR = j; // 更新pR 和 index
index = i;
}
else{
if (index_left < _i_left)
{
p[i] = p[_i];
}
else if (index_left > _i_left){
p[i] = index_right - i + 1;
}
else//if (_i_left == index_left)
{
// 往外擴
int j = pR;
int _j = i - (pR - i);
while (str[_j] == str[j] && _j >= 0 && j < len)
{
_j--;
j++;
if (_j < 0 || j >= len)
break;
}
p[i] = j - i;
pR = j; // 更新pR 和 index
index = i;
}
}
if (p[i] - 1 > maxlen)
maxlen = p[i] - 1;
}// for
return maxlen;
}
};
Longest Palindromic Substring (leetcode)
ac程式碼
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if (n <= 1)
return s;
// 新增特殊字元
string str;
for (int i = 0; i < n; i++){
str += "#";
str += s[i];
}
str += "#";
int len = 2 * n + 1;
vector<int> p(len); // p[i]為迴文中心半徑陣列(包括以str[i])
int index; // 迴文中心位置, pR更新時 index跟著跟新; pR不更新,index保持不變
int pR; // // 迴文中心最右邊的 下一個
int maxlen = 1; //最大回文長度變數
int pos; // 記錄最大回文的中心位置
// i=0, i=1單獨處理
p[0] = 1;
p[1] = 2;
index = 1;
pR = 3;
pos = 1;
for (int i = 2; i < len; i++)
{
int _i = index - (i - index);
int index_left = index - p[index] + 1;
int index_right = pR - 1;
int _i_left = _i - p[_i] + 1;
if (_i_left < index_left) // 1
{
int j = i + 1;
int _j = i - 1;
while (str[j] == str[_j] && _j >= 0 && j < len)
{
_j--;
j++;
if (_j < 0 || j >= len)
break;
}
p[i] = j - i;
pR = j;
index = i;
}
else{
if (index_left < _i_left){ // 2
p[i] = p[_i];
}
else if (index_left > _i_left){ // 3
p[i] = index_right - i - 1;
}
else//if (_i_left == index_left) // 4
{
// 往外擴
int j = pR;
int _j = i - (pR - i);
while (str[_j] == str[j] && _j >= 0 && j < len)
{
_j--;
j++;
if (_j < 0 || j >= len)
break;
}
p[i] = j - i;
pR = j; // 更新pR 和 index
index = i;
}
}
if (p[i] - 1 > maxlen){
maxlen = p[i] - 1;
pos = i;
}
}// end for
// 得到子串
int realpos = pos / 2;
int startpos = realpos - maxlen / 2;
string ans = s.substr(startpos, maxlen);
return ans;
}
};