LeetCode(005)- Longest Palindromic Substring

题目: Longest Palindromic Substring

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”

翻译:

给定一个字符串s,找出s中最长的回文子串。可以假设s的最大长度为1000。
示例1:
输入:“babad”
输出:“巴布”
注:“aba”也是一个有效的答案。
示例2:
输入:“cbbd”
输出:“bb”

什么是回文串:

子串:小于等于原字符串长度由原字符串中任意个连续字符组成的子序列
回文:关于中间字符对称的文法,即“aba”(单核)、“cabbac”(双核)等
最长回文子串:

  1. 寻找回文子串;
  2. 该子串是回文子串中长度最长的。
方法一: 移动滑块从外向内找回文串:先找最长的,下来一次递减;
时间复杂度O(n3) ;
代码实现:
class Solution {
    public boolean Palindrome(String s,int left,int right){
         while(left<right){
             if(s.charAt(left++)!=s.charAt(right--))
                 return false;
         }
        return true;
    }
    public String longestPalindrome(String s) {
        int len=s.length();
        if(s==null || len < 2)
            return s;
        int len1=len; 
        //从长到短判断回文子串
        while(len1>0){
            for(int i=0 ;i<=len-len1 ; i++){
                if(Palindrome(s,i,i+len1-1))
                    return s.substring(i,i+len1);
            }
            len1--;
        }
        return "";
    }
}
方法二:动态规划,从中心向外进行扩散;
时间复杂度: O(n2);
思路:我们将s中任何一点(奇数回文串是一点用i表示,偶数是两点用i和i+1表示)作为回文串的中点,然后设置left和right表示其中点左右坐标;那么当left和right的元素相同时,回文串的长度加2,此时与已知的最长回文子串进行比较,找到最长的回文串;
class Solution {
    int maxLen=0; //最长回文串长度
    int start=0;//回文串开始角标
    int len=0;
    public void Palindrome(String s,int left,int right){
        while(left>=0&&right<len&&s.charAt(left--)==s.charAt(right++));
        if(maxLen<right-left-1){ //判断当前子串和最长子串的长度
            start=left+1;
            maxLen=right-left-1;
        }      
    }
    public String longestPalindrome(String s) {
        len=s.length();
        if(s==null || len < 2) //如果只有一个元素或者没有元素
            return s;
        for(int i=0 ; i<len-1 ; i++){ 
            Palindrome(s,i,i);//奇数
            Palindrome(s,i,i+1);//偶数
        }
        return s.substring(start,start+maxLen); 
       
    }
}
方法三:Manacher算法,算法思想马拉车算法详解,请点击
时间复杂度:O(n):
步骤:
  1. 用字符串中没有的元素(一般可以选#)将每个元素分隔开来,并存出集合中;
  2. 构建一个包含分割元素长度的数组,值为以数组角标为中心的的回文串的一半长度;
  3. 遍历集合时,每次找到左边与j相对于最右回文串的角标;
  4. 对角标的值进行判断,如果角标代表的回文串完全包含在最右回文串里面,那么j角标对应的值与左边的值一样;
  5. 如果只是部分包含,那么只需要判断未包含部分什么时候会形成以j为中心的最长回文串;
  6. 如果j都没有在最右回文串里面,那么以j为中心,左右遍历找到回文串;
代码实现:
class Solution {
    public int Palindrome(List<Character> list, int mid,int l,int r){
        //匹配得到回文串
        while(l>=0&&r<list.size()&&list.get(l).equals(list.get(r))){
            //集合里面是包装类,不要用==
            l--;
            r++;
        }
        if(r==mid+1)
            return 0;
        return r-mid-1;//中点右边元素个数
    }
    public String longestPalindrome(String s) {
        int len=s.length();
        List<Character> list=new ArrayList(len*2+1);
        for(int i=0 ;i<len ;i++){
            list.add('#');
            list.add(s.charAt(i));
        }
        list.add('#');
        int[] ln=new int [len*2+1];
        int r=-1; //最右回文串右端角标
        int mid=-1;//最右回文串中点坐标
        int maxMid=-1;//最长回文串中间坐标
        int maxLen=-1;//最长回文串一端长度;
        for(int j=0 ; j <len*2+1 ; j++){
            if(j<r){
                int leftLen=ln[2*mid-j];
                if(leftLen< r-j){ 
                    //对称左边回文串长度没有超出以r为右端点的回文串范围
                    ln[j]=leftLen;
                }else{ 
           //对称左边回文串长度超出范围不知道匹配不匹配,对超出部分进行匹配校验
                    ln[j]=Palindrome(list,j,j-(r-j),j+r-j);
                }
            }else{
                //完全没有左边堆成回文串需要无脑匹配
                ln[j]=Palindrome(list,j,j-1,j+1);//一半长度;
            }
            if(j+ln[j]>r){ //当前回文串右端与保存的右端取大
                r=j+ln[j];
                mid=j;
            }
            if(maxLen<ln[j]){ //得到目前最长回文子串
                maxLen=ln[j];
                maxMid=j;
            }
        }
        StringBuilder sb=new StringBuilder();
        for(int z=maxMid-maxLen ; z<= maxMid+maxLen ;z++){
            //去除#,得到最长回文子串
            char ch=list.get(z).charValue(); 
            if(ch!='#')
                sb.append(ch);
        }
        return sb.toString();
    }
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值