1. 程式人生 > >leetcode關於迴文類題目的總結

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