【leetCode】之 Longest Palindromic Substring

使用方法:Manacher方法(参考博客中叫做——马拉车算法,很幽默的博主。。)

参考博客:https://blog.csdn.net/HappyRocking/article/details/82527217

                  https://blog.csdn.net/HappyRocking/article/details/82622881

在这里鸣谢此博主,讲的通俗易懂,看过思想之后,代码写得很顺畅~

题目:

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"
Output: "bb"

分析:此题所求为最长回文子串,也就是字符串是的 子串 中 长度最长的 回文字符串

方法一:暴力求解(时间复杂度:O(n^3))

    思路:满足上面的两个条件,(1)求出字符串的子串s'  (2)判断 s' 是否为回文字符串 

    C++代码:(当然仅作理解)

​
class Solution {
public:
    string longestPalindrome(string s) {
        
        int n = s.size();
        if(n == 0) return "" ;
        
        string res = s.substr(0,1) ;
        
        /*
          求子串:
          i 代表要求的子串长度 ,这里从n开始递减,So只要找到第一个就是最长的回文子串
          j 代表子串的起始位置,注意边界问题
          由于两层for循环,所以当res值不为空时,就证明已有最长回文子串,可结束循环
        */
        for(int i = n ; i > 1 ; i --){ 
            for(int j = 0 ; j <= n - i ; j ++ ){
                if(isPalindromic(s,j,i)){
                    res = s.substr(j,i);
                    break ;
                }
            }
            if(res.size() != 1) break;
        }
        return res ;
    }
    
    // 判断字符串是否为回文串
    int isPalindromic(string s, int i, int k){
        int j = i + k - 1;
        while(i < j){
            if(s[i] == s[j]){
                i ++;
                j --;
            }
            else
                return 0;
        }
        return 1;
    }
};

​

方法二:中心扩展法(时间复杂度:O(n^2))

    思路:从所谓的中心字符开始向外扩充,直到两字符不同

              例:s = "asaffa" 

              1) 单字符串为中心:当‘s’为中心字符时 ,初始时回文串是‘s’本身,之后向两面扩大会问字符串长度——即判断s两边的字符是否相等,若相等则扩大半径。。。(可自行想象)

              2)双字符串为中心:当‘ff’为中心字符串时,初始回文串是‘ff’,同样的向两侧扩展

             想想这种方法要考虑到每次的中心字符为  一个  和  两个  的情况,但是第一层for循环s[i]的意义就是中心字符,两个中心字符的情况就考虑s[i]s[i + 1] ,所以就要第一层for循环最大长度为s.size() - 1,有人可能会想若中心字符为1个时,那么最后一个字符没考虑是不是对答案有影响?答案是NO,因为若最后一个字符为中心字符,最后的结果也就是它本身,而在他之前一定已经有了一个答案~   具体过程详见代码

    C++代码:

​
class Solution {
private:  //最后结果,设为全局变量,方便更改,也可做为局部变量,修改过程需传参
    string res = "" ;
    
public:
    string longestPalindrome(string s) {
        //由于第一层遍历时到最后一个字符的前一位
        //所以空值和一个字符时并没处理
        //方便的是这两种情况可直接返回答案
        if(s.size() <= 1) return s ;
        
        for(int i = 0 ; i < s.size() - 1 ; i ++){
           
            freshPalindromic(s,i,i) ;
            freshPalindromic(s,i,i + 1) ;

        }
        
        return res ;
    }
    
    更新全局变量——最后的回文串结果
    void freshPalindromic(string s, int i, int j){
        
        while(i >= 0 && j < s.size())
            if(s[i] == s[j]){
                i -- ;
                j ++ ;
            }
            else
                break ;
        //无论是遍历到边界还是由于两字符不等退出while循环,都需要更改ij的值
        i ++ ;
        j -- ;
        
        if(j - i + 1 > res.size())
            res = s.substr(i,j - i + 1) ;

    }
};

​

方法三:Manacher法(时间复杂度:O(n))

    思路:还是借鉴大神的思路:https://blog.csdn.net/HappyRocking/article/details/82622881

    C++代码:

​
class Solution {
public:
    string longestPalindrome(string s) {
        int p[2005];  //标记以 ss[i] 为中心的回文串半径 
        int id = 0, mx = 0 ;
        string ss = "#" , res = "";
        
        //插入特殊字符
        for(int i = 0 ; i < s.size() ; i ++){
            ss += s[i] ;
            ss +="#" ;
        }
        
        for(int i = 0 ; i < ss.size() ; i ++){
            if(i >=mx)
                p[i] = 0 ;
            else
                p[i] = min(mx - i , p[2*id - i]) ;
            
            if(i >= mx || p[i] == mx - i){ //中心扩展法向外扩充
                while(ss[i + p[i]] == ss[i - p[i]] && i + p[i] + 1 < ss.size() && i - p[i] - 1 > -1) 
                    p[i] ++ ;
            }
            
            //由于判断是否越界问题,可能导致p[i]多加1,判断后减1
            if(ss[i + p[i]] != ss[i - p[i]]) p[i]--;
            //更新最长回文子串中心值与最后位置的值
            if(p[i] > mx - id){
                mx = i + p[i] ;
                id = i ;
            }
        }

        //输出
        for(int i = 2*id - mx; i <= mx ; i ++){
            if(ss[i] != '#')
                res += ss[i] ;
        }
            
        return res ;
    }
};

​

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值