寻找最长回文数

该博客探讨如何找到给定字符串的最长回文子串,介绍中心扩展算法和动态规划等解决策略,例如在字符串"aabcdcb"中发现"bcdcb",在"bananas"中找到"anana"。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

给定一个字符串,找到最长的回文连续子字符串。如果不止一个,则返回一个即可。例如,“aabcdcb”的最长回文子串是“bcdcb”。 “bananas”的最长回文子串是“anana”。

中心扩展算法

 public static String longestPalindrome(String s) {
        if (s == null || s.length() < 1) {
            return "";
        }

        int start = 0;
        int end = 0;
        for (int i = 0; i < s.length(); i++) {
            // 精髓在于此,len1,i与i比较,对应于奇数个数,中心对应i这个数,向两边扩散。aca
            // len2,以此类推,对应于偶数个数,中心对应i和i+1中间的空格,相当于已经扩散了一次,已经在i和i+1上,向两边扩散。fddf
            int len1 = expandAroundCenter(s, i, i); // 检测长度为奇数的子串, 例如aba
            int len2 = expandAroundCenter(s, i, i + 1); // 检测长度为偶数的子串, 例如abba
            int len = Math.max(len1, len2);
            if (len > end - start) {
                // 假设回文串长度为奇数,start与i之间的距离为(2n+1)/2;
                // 假设回文串长度为偶数,start与i的距离为(2n)/2-1;
                // 而在整数除法里,(2n)/2-1与(2n-1)/2是相等的;(2n+1)/2与(2n)/2的结果是相等的;
                // 于是start与i之间的距离得以统一;对于end来说,分析是一致的
                start = i - (len - 1) / 2; // 扩展后, 以i为中心,左指针前移一半长度
                end = i + len / 2; // 扩展后, 以i为中心,右指针后移一半长度
            }
        }

        return s.substring(start, end + 1);
    }

    public static int expandAroundCenter(String s, int left, int right) {
        //left:相当于指向中间位置的第一位;
        //right:相当于指向中间位置最后一位;
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
        }

        // 返回回文数的长度
        // 长度为(right - 1) - (left + 1 ) + 1 = right - left - 1
        return right - left - 1;
    }

动态规划算法

 public static String longestPalindrome(String s) {
        int len = s.length();

        if (len == 1) {
            return s;
        }

        int maxLen = 1;
        int begin = 0;
        // dp[i][j] 表示 s[i..j] 是否是回文串
        boolean[][] dp = new boolean[len][len];
        // 初始化:所有长度为 1 的子串都是回文串
        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }

        char[] charArray = s.toCharArray();
        // 递推开始
        // 先枚举子串长度,先枚举长度为2的所有子字符串,再依次枚举长度为3,4,5...len所有子字符串
        for (int L = 2; L <= len; L++) {
            // 枚举左边界,左边界的上限设置可以宽松一些
            for (int i = 0; i < len; i++) {
                // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                int j = L + i - 1;
                // 如果右边界越界,就可以退出当前循环
                if (j >= len) {
                    break;
                }

                if (charArray[i] != charArray[j]) {
                    dp[i][j] = false;
                } else {
                    if (j - i < 3) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                // 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substring(begin, begin + maxLen);

    }

方法3

    public static String longestPalindrome(String s) {

        if (s == null || s.length() < 1) {
            return "";
        }
        int len = s.length();
        int max = 0;
        String res = "";
        for (int i = 0; i < len; i++) {
            int left = i - 1;
            int right = i + 1;

            while (left >= 0 && s.charAt(left) == s.charAt(i)) {
                left--;
            }

            while (right < s.length() && s.charAt(right) == s.charAt(i)) {
                right++;
            }

            while (left >= 0 && right < len && s.charAt(left) == s.charAt(right)) {
                left--;
                right++;
            }

            // 经过以上判断left和right指向的值一定不同,且包含的子串一定是回文串

            if (max < right - left - 1) {
                max = right - left - 1;
                res = s.substring(left + 1, right);
            }
        }
        return res;
    }

方法4

  public static String longestPalindrome(String s) {

        if (s == null || s.length() < 1) {
            return "";
        }
        int len = s.length();
        int max = 0;
        int start = 0;
        for (int i = 0; i < len; i++) {
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int curMax = Math.max(len1, len2);
            if (max < curMax) {
                max = curMax;
                start = i - (curMax + 1) / 2 + 1;
            }
        }
        return s.substring(start, start + max);
    }
    public static int expandAroundCenter(String s, int left, int right) {
        //left:相当于指向中间位置的第一位;
        //right:相当于指向中间位置最后一位;
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
        }

        // 返回回文数的长度
        // 长度为(right - 1) - (left + 1 ) + 1 = right - left - 1
        return right - left - 1;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值