leetcode刷题之最长回文子串

--------题目描述

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例2:

输入: "cbbd"
输出: "bb"

--------解题思路

看到这个题目,我这个菜鸡第一反应是暴力求解。遍历所有子字符串,然后判断是否回文。这显然时间复杂度最少为 O ( n 2 ) O(n^{2}) O(n2)。判断回文还需要遍历子字符串,这就造成 O ( n 3 ) O(n^{3}) O(n3)复杂度。代码我肯定可以写出来,但是,这不是我想要的结果。我是为了学算法而刷题的,而不是为了写代码而刷!
在官方给的动态规划法提示下,我看到了减少暴力求解时间复杂度的方法。

  • 动态规划法求解这个,首先考虑清楚,回文字符串长度有奇偶之分,最长奇数回文字符串都是从长度为1的回文子串开始两边慢慢往外扩展得到的;最长偶数回文字符串也是从长度为2的回文子串开始两边慢慢扩展得到的。
    1.如果没有长度为2的回文子串,就不会存在长度为4的回文子串;
    2.如果没有长度为3的回文子串,就不会存在长度为5的回文子串;
    这个道理,想必一般人都知道,没想到的你可以看看回文串的定义,然后在草稿纸上划拉划拉。
    理清楚上面的,那么接下来就是算法步骤
    初始化长度为1和2的回文串,然后在长度为1的基础上,找出所有长度为3的回文子串,在长度为2的基础上,找出所有长度为4的回文子串, . . . . . . ...... ......,依次直到最后找到长度为原字符串长度的回文串。

下面贴上我那拙劣的java代码:

class Solution {
    public String longestPalindrome(String s){
        /*动态规划法,遍历所有情况*/
        if(s.length()==0){return s;}
        int indexRight=0,indexLeft=0;  //定义最大回文串左边界和右边界
        int len = 1;  //首先从子字符串长度为1的开始
        /*长度为奇数的回文子串的左边界和右边界*/
        HashMap<Integer,Integer> oddIndex = new HashMap<Integer,Integer>();

        /*长度为偶数的回文子串的左边界和右边界*/
        HashMap<Integer,Integer> evenIndex = new HashMap<Integer,Integer>();
        //复制品
        HashMap<Integer,Integer> shadow = new HashMap<Integer,Integer>();
        /*初始化回文子串为1和2的*/
        for(int i=0;i<s.length();++i){
            oddIndex.put(i,i);
            if(i<s.length()-1&&s.charAt(i)==s.charAt(i+1)){
                evenIndex.put(i,i+1);
                indexLeft = i;
                indexRight = i+1;

            }
        }
        while(len<s.length()){
            Iterator<HashMap.Entry<Integer,Integer>> it ;
            if(len%2!=0){
                 it = oddIndex.entrySet().iterator();
            }else{
                it = evenIndex.entrySet().iterator();
            }

            while(it.hasNext()){
                int i=1;
                HashMap.Entry<Integer,Integer> entry = it.next();
                while(entry.getKey()-i>=0&&entry.getValue()+i<s.length()&&s.charAt(entry.getKey()-i)==s.charAt(entry.getValue()+i)){
                    ++i;
                }
                if(i!=1){
                    shadow.put(entry.getKey()-i+1,entry.getValue()+i-1);
                    if((entry.getValue()+2*i-2-entry.getKey())>(indexRight-indexLeft)){
                        indexLeft = entry.getKey()-i+1;
                        indexRight = entry.getValue()+i-1;
                    }

                }
            }

            if(len%2!=0){
                oddIndex = shadow;
            }else{
                evenIndex = shadow;
            }
            shadow.clear();
            ++len;
        }

        return s.substring(indexLeft,indexRight+1);
    }
}

以上代码就是按照上面思想算的,这样比暴力解法好的地方就是,有条件的穷举。例如在穷举回文串时,当字符串中不存在长度为3的回文子串时,那后面奇数长度的就不需要遍历了,省去了遍历时间。
下面是提交结果:
在这里插入图片描述

  • 马拉车算法
    该算法思想也比较简单,就是利用回文的对称特性,减少不必要的迭代。
    这个算法是专门用来解决找回文串的,我想大家应该也知道算法思想吧,我这里就不废话了。下面是我拙劣的代码:
public String reconStr(String s){
        /*函数功能是向输入字符串插入#和$
        eg: input:"abedce" --> output:"$#a#b#e#d#c#e#"
        * */
        StringBuilder str = new StringBuilder(s);

        for(int i=s.length();i>=0;i--){str.insert(i,'#');}

        str.insert(0,'$');
        return str.toString();
    }

    public String longestPalindrome(String s) {
        s = reconStr(s);

        /*接下来就是计算p[i]数组*/
        int MaxRight=0,pos=0,max=0;
        int[] RL = new int[s.length()];
        for(int i=1;i<s.length();++i){
            if(i<MaxRight){RL[i] = RL[2*pos-i]<(MaxRight-i)?RL[2*pos-i]:(MaxRight-i);}
            else{RL[i] = 1;}
            /*不在MaxRight覆盖范围,那就只能一步一步外扩*/
            while(i-RL[i]>=0&&i+RL[i]<s.length()&&s.charAt(i-RL[i])==s.charAt(i+RL[i])){++RL[i];}

            /*更新pos和MaxRight*/
            if(i+RL[i]-1>MaxRight){
                pos = i;
                MaxRight = RL[i]+i-1;
            }
            if(RL[i]>RL[max]){max = i;}
        }

        StringBuilder result = new StringBuilder();
        for(int i=max-RL[max]+1,j=0;i<=RL[max]+max-1;++i){
            if(s.charAt(i)!='#'&&s.charAt(i)!='$'){
                result.append(s.charAt(i));
                ++j;
            }

        }

        return result.toString();
    }

提交结果如下:
在这里插入图片描述

反思

马拉车算法我看了一天,才写出来。太笨了。之前学数据结构的时候没听过这个算法。算法比较简洁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值