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



 
 

猜你喜欢

转载自blog.csdn.net/gujun5168698/article/details/79747808