leetcode關於迴文類題目的總結
1. 給定一個字串 s,找到 s 中最長的迴文子串。你可以假設 s 長度最長為1000。
示例:
輸入: "babad" 輸出: "bab" 注意: "aba"也是有效答案
示例:
輸入: "cbbd" 輸出: "bb"
三種解題思路,第一種遍歷所有字串,暴力法,複雜度o(n3),這裡就不講了;第二種,觀察發現迴文都是對稱,由中心向兩邊擴充套件,可以遍歷每個元素,計算最大的擴充套件寬度,當然需要考慮,偶數和奇數迴文,複雜度o(n2);第三種,是在第二種上進行優化,也就是Manacher演算法,複雜度O(n),具體演算法分析請看https://www.felix021.com/blog/read.php?2040
Manacher演算法
string processStr(string s){ string str(2*s.length() + 1, '#'); for(int i = 0; i < s.length(); i++) str[2*i+1] = s[i]; return str; } string longestPalindrome(string s) { string str = processStr(s); vector<int> vec_p(str.size(),1); int max_d = 0; int max_right = 0; int max_length = 1; int begin = 0; for(int i = 0; i < str.size(); i++){ if(i < max_right){ vec_p[i] = vec_p[2*max_d - i]< max_right - i ?vec_p[2*max_d - i]: max_right - i; } else vec_p[i] = 1; while(i - vec_p[i] >= 0 && i + vec_p[i] < str.size() &&str[i - vec_p[i]] == str[i + vec_p[i]]) vec_p[i]++; if(vec_p[i] + i - 1 > max_right){//因為最少長度為1 max_right = vec_p[i] + i - 1; max_d = i; } if(max_length < vec_p[i]){ max_length = vec_p[i]; begin = i - max_length + 1; } } string str_ret = str.substr(begin,2*max_length - 1); str_ret.erase(std::remove(str_ret.begin(), str_ret.end(), '#'), str_ret.end()); return str_ret; }
//中心點擴散法
string findLongestPalindrome(string &s)
{ const int length=s.size(); if(length == 1)return s; if(length == 0)return NULL; int maxlength=0; int start; for(int i=0;i<length;i++)//長度為奇數 { int j=i-1,k=i+1; while(j>=0&&k<length&&s.at(j)==s.at(k)) { if(k-j+1>maxlength) { maxlength=k-j+1; start=j; } j--; k++; } } for(int i=0;i<length;i++)//長度為偶數 { int j=i,k=i+1; while(j>=0&&k<length&&s.at(j)==s.at(k)) { if(k-j+1>maxlength) { maxlength=k-j+1; start=j; } j--; k++; } } if(maxlength>0) return s.substr(start,maxlength); return NULL; }
2.給一個字串 S, 你可以通過在字串前面新增字元將其轉換為迴文串。找到並返回可以用這種方式轉換的最短迴文串。
例如:
給出 "aacecaaa"
,返回 "aaacecaaa"
。
給出 "abcd"
,返回 "dcbabcd"
。
該題主要要理解題意,其實就是求迴文的中心位置,該位置必須保證從原字串第一個字元開始,所以可以用題一種的擴散法和Manacher演算法改進一下求解,下面列出Manacher演算法解題程式碼
class Solution {
public:
string shortestPalindrome(string s) {
string str = processStr(s);
vector<int> vec_p((str.size()+1)/2,1);
int max_d = 0;
int max_right = 0;
int cent = 0;
for(int i = 0; i < (str.size()+1)/2; i++){
if(i < max_right){
vec_p[i] = vec_p[2*max_d - i]< max_right - i ?vec_p[2*max_d - i]: max_right - i;
}
else
vec_p[i] = 1;
while(i - vec_p[i] >= 0 && i + vec_p[i] < str.size() &&str[i - vec_p[i]] == str[i + vec_p[i]])
vec_p[i]++;
if(vec_p[i] + i - 1 > max_right){//因為最少長度為1
max_right = vec_p[i] + i - 1;
max_d = i;
}
if(i + 1 == vec_p[i]){//注意這裡表示必須能擴散到字串頭部。
//max_length = vec_p[i];
cent = i;
}
}
string str_add = str.substr(vec_p[cent]*2 - 1,str.size() - vec_p[cent]*2 + 1);//需要補全的
int add_len = str_add.size();
string new_str(add_len,' ');
for(int i = 0; i < add_len; i++)//反轉
new_str[i] = str_add[add_len-1-i];
new_str += str;
new_str.erase(std::remove(new_str.begin(), new_str.end(), '#'), new_str.end());
return new_str;
}
string processStr(string s){
string str(2*s.length() + 1, '#');
for(int i = 0; i < s.length(); i++)
str[2*i+1] = s[i];
return str;
}
};
3.
給定一組
獨特
的單詞, 找出在給定列表中
不同
的索引對
(i, j)
,使得關聯的兩個單詞,例如:
words[i] + words[j]
形成迴文。
示例 1:
給定 words
= ["bat", "tab", "cat"]
返回 [[0, 1], [1, 0]]
迴文是 ["battab", "tabbat"]
示例 2:
給定 words
= ["abcd", "dcba", "lls", "s", "sssll"]
返回 [[0, 1], [1, 0], [3, 2], [2, 4]]
迴文是 ["dcbaabcd", "abcddcba", "slls", "llssssll"]
該題解題思路暫時只想到兩種,一種就是暴力搜尋,程式碼就不貼出來了。
第二種藉助hash map,思路是將每個字串進行迴圈分割,如果一部分為迴文串,另一部分不是,則只需要搜尋能否找到非迴文串的反轉穿,找到即可配對一組,程式碼如下:
class Solution {
public:
bool isvalid(string s){ //判斷是否為迴文串
int i = 0, j = s.size()-1;
while(i < j){
if(s[i] != s[j])
return false;
i++;
j--;
}
return true;
}
vector<vector<int>> palindromePairs(vector<string>& words) {
vector<vector<int>>res;
map<string, int>map_str;
for(int i = 0; i < words.size(); i++)
map_str[words[i]] = i;
for(int j = 0; j < words.size(); j++){
reverse(words[j].begin(), words[j].end());
int len = words[j].size();
for(int k = 0; k <= len; k++){
string left = words[j].substr(0,k);
string right = words[j].substr(k);
if(map_str.count(left) && isvalid(right)&&(map_str[left] != j)&&(k < len)) //從前面連線
res.push_back(vector<int>{map_str[left], j});
if(map_str.count(right) && isvalid(left)&&(map_str[right] != j)) //從後面連線
res.push_back(vector<int>{j, map_str[right]});
}
}
return res;
}
};