【LeetCode】最长回文子串

问题
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
扩展中心法
1、最长子串的长度可能是奇数,也可能是偶数个。所以我们需要选取一个字符或两个字符作为中心进行扩散
2、找到关于中心对称的最长子字符串即为所求
马拉车算法
该算法改进了扩展中心算法(以下内容可能与原始算法有出入)。
1、为了避免处理奇数长度和偶数长度,先对原字符串进行处理。在原字符串的基础上间隔插入符号’#’(该符号即使会出现在原字符串也不受影响)。形如:

abc → #a#b#c#
ab  → #a#b#c

这么做可以保证我们选取一个字符作为中心时,考虑到了所有可能的中心点,原字符不用考虑,新增的’#'实际上是考虑了以两个元素作为中心。
2、用数组r[i]记录每个元素作为中心时,最长对称子串的半径(不包括自身,即字符串a的半径为0)
该算法核心的地方就是借用了之前的r[i]内容已达到减少重复计算的目的。
position——当前对称子串长度最长的中心位置
rm——最长对称子串的半径
right——以position为中心,能达到的最右侧位置

if(i<right) {
    int j = 2*position-i; //对称位置
    if(right<i+r[j]) {  //超出界线
        r[i] = right-i;
    } else {
        r[i]=r[j]; //不一定是最终结果
    }               
}

以上代码解释了应该如何借助已有半径来计算r[i]。

  • 如果i>=right,那么就无法借用已知信息,只能以此为中心向两侧扩展。
  • 如果i<right,那么可以借用r[j]来初始化r[i],已达到减少重复计算的目的(j是i关于position对称的位置)。此时也会有两种情况:
    (1)以j为中心的最长对称子串全部在以position为中心的最长子串范围,则r[i]=r[j],考虑到可能到达边界,因此还需要进一步确认是否以为最长;
    (2)以j为中心的最长对称子串有部分不在以position为中心的最长子串范围,则超出部分不保证关于position对称,则r[i]=right-i,然后再去按照扩展中心法进一步调整r[i]
    3、截取最长回文子串,完整的代码如下:
class Solution {
    public String longestPalindrome(String s) {
        if(s.equals("")) {
            return "";
        }
        
        //预处理
        StringBuilder sb = new StringBuilder();
        for(int i=0 ; i<s.length() ; i++) {
            sb.append('#');
            sb.append(s.charAt(i));            
        }
        sb.append('#');

        int[] r = new int[sb.length()];
        int position = 0;//记录拥有最长回文串的中心
        int rm = 0;//记录最长回文子串的半径
        int right = position+rm ;//记录最长回文子串能到的最右边的位置
        for(int i=0 ; i<sb.length() ; i++) {
            if(i<right) {
                int j = 2*position-i; //对称位置
                if(right<i+r[j]) {  //超出界线
                    r[i] = right-i;
                } else {
                    r[i]=r[j]; //不一定是最终结果
                    
                }
                
            }
            int start = i+r[i]+1;
             while(true) {
                if(2*i-start<0||start>=sb.length()) {
                    break;
                } else  {
                    if(sb.charAt(2*i-start)!=sb.charAt(start)) {
                        break;
                    } else {
                        r[i]++;
                        start++;
                    }
                }

            }
            if(r[i]>rm) {
                rm = r[i];
                position = i;
                right = position+rm;
            }
        }
        String tmp = sb.substring(2*position-right,right+1);
        StringBuilder res = new StringBuilder();
        for(int i=0 ; i<tmp.length() ; i++) {
            if(tmp.charAt(i)!='#') {
                res.append(tmp.charAt(i));
            }
        }
        return res.toString();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值