LeetCode005:最长回文子串 中心扩散法

给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。

思路:使用中心扩散法,「中心扩散法」的基本思想是:遍历每一个下标,以这个下标为中心,利用「回文串」中心对称的特点,往两边扩散,看最多能扩散多远。
回文串在长度为奇数和偶数的时候,「回文中心」的形态不一样:

  • 奇数回文串的「中心」是一个具体的字符,例如:回文串 “aba” 的中心是字符 “b”;

  • 偶数回文串的「中心」是位于中间的两个字符的「空隙」,例如:回文串 “abba” 的中心是两个 “b”,也可以看成两个 “b” 中间的空隙。

复杂度分析:
枚举「中心位置」时间复杂度为O(N),从「中心位置」扩散得到「回文子串」的时间复杂度为 O(N),因此时间复杂度可以降到 O(N^2)
时间复杂度:O(N^2),这里 NN 是字符串的长度;
空间复杂度:O(1),只使用到常数个临时变量,与字符串长度无关。

在这里插入图片描述
我们可以设计一个方法,兼容以上两种情况:
如果传入重合的下标,进行中心扩散,此时得到的回文子串的长度是奇数;
如果传入相邻的下标,进行中心扩散,此时得到的回文子串的长度是偶数。

public String longestPalindrome(String s) {
        int len = s.length(); //数组长度
        if(len < 2) return s; //数组小于2,直接返回
        
        int maxLen = 0; //回文最大长度
        // 数组第一位记录起始位置,第二位记录长度
        int[] res = new int[2];
        //这里s.length()-1可能会有疑问,以abcba为例,判断那里相当于i<4,循环到下标3,0123长度为4,最后一个不需要循环到的。
        for (int i = 0; i < s.length() - 1; i++) {
        	//以s[i]为中心的最长回文子串,奇数
            int[] odd = centerSpread(s, i, i);
            //以s[i]和s[i+1]为中心的最长回文子串
            int[] even = centerSpread(s, i, i + 1);
            int[] max = odd[1] > even[1] ? odd : even;
            if (max[1] > maxLen) {
                res = max;
                maxLen = max[1];
            }
        }
        return s.substring(res[0], res[0] + res[1]);
    }

    private int[] centerSpread(String s, int left, int right) {
        int len = s.length();
        while (left >= 0 && right < len) {  //左边有值且右边不超边界
            if (s.charAt(left) == s.charAt(right)) { //左右字符一致
                left--; //左减
                right++; //右加
            } else {
                break;
            }
        }
        return new int[]{left + 1, right - left - 1}; 
    }

下面代码执行速度更快
在这里插入图片描述

public String longestPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return "";
        }
        //保存起始位置,测试了用数组似乎能比全局变量稍快一点
        int[] range = new int[2];
        char[] str = s.toCharArray();
        for (int i = 0; i < s.length(); i++) {
            //把回文看成中间的部分全是同一字符,左右部分相对称
            //找到下一个与当前字符不同的字符
            i = findLongest(str, i, range);
        }
        //substring方法含头不含尾,所以需要加一
        return s.substring(range[0], range[1] + 1);
    }

    public static int findLongest(char[] str, int low, int[] range) {
        //查找中间部分
        int high = low;
        while (high < str.length - 1 && str[high + 1] == str[low]) {
            high++;
        }
        //定位中间部分的最后一个字符
        int ans = high;
        //从中间向左右扩散
        while (low > 0 && high < str.length - 1 && str[low - 1] == str[high + 1]) {
            low--;
            high++;
        }
        //记录最大长度
        if (high - low > range[1] - range[0]) {
            range[0] = low;
            range[1] = high;
        }
        return ans;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值