计算最长回文子串

问题

寻找最长回文子串是一个经典的面试编码问题。在这篇文章中,将为这个问题总结3种不同的解决方案。

解决方案

1. 朴素方法

我们可以简单地检查每个子字符串并检查它是否是回文的。时间复杂度为O(n3)。如果这是提交给LeetCode onlinejudge,将返回一个错误消息“时间限制超过”。因此,这种方法只是一个开始,我们需要一个更好的算法。

public static String longestPalindrome1(String s) {
        int maxPalinLength = 0;
        String longestPalindrome = null;
        int length = s.length();

        // 遍历所有可能的子串
        for (int i = 0; i < length; i++) {
            for (int j = i + 1; j < length; j++) {
                int len = j - i + 1;
                String curr = s.substring(i, j + 1);
                if (isPalindrome(curr)) {
                    if (len > maxPalinLength) {
                        longestPalindrome = curr;
                        maxPalinLength = len;
                    }
                }
            }
        }
        return longestPalindrome;
    }
public static boolean isPalindrome(String s) {
        for (int i = 0; i < s.length() - 1; i++) {
            if (s.charAt(i) != s.charAt(s.length() - 1 - i)) {
                return false;
            }
        }
        return true;
    }

2. 动态规划法

假设s是输入字符串,i和j是字符串的两个下标。定义一个二维数组“table”,让table[i] [j]表示从i到j的子串是否为回文。

初始情况:

table[i] [i] == 1;
table[i] [i+1] == 1 => s.charAt(i) == s.charAt(i+1)

变化条件:

table[i+1] [j-1] == 1 && s.charAt(i) == s.charAt(j)=>table[i] [j] == 1

时间O(nˆ2),空间O(nˆ2)。

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

    int maxLen = 0;
    String longestStr = null;
    int length = s.length();
    int[][] table = new int[length][length];
    // 初始情况 对角线均为1 即单个字符是回文的
    for (int i = 0; i < length; i++) {
        table[i][i] = 1;
    }
    printTable(table);
    // 考虑回文长度为2
    for (int i = 0; i < length - 2; i++) {
        if (s.charAt(i) == s.charAt(i + 1)) {
            table[i][i + 1] = 1;
            longestStr = s.substring(i, i + 2);
        }
    }
    printTable(table);
    // 计算回文长度为3-length的情况
    for (int l = 3; l <= length; l++) {
        for (int i = 0; i <= length - l; i++) {
            int j = i + l - 1;
            if (s.charAt(i) == s.charAt(j)) {
                table[i][j] = table[i + 1][j - 1];
                if (table[i][j] == 1 && l > maxLen) {
                    longestStr = s.substring(i, j + 1);
                } else {
                    table[i][j] = 0;
                }
                printTable(table);
            }
        }
    }
    return longestStr;
}

public static void printTable(int[][] x) {
    for (int[] y : x) {
        for (int z : y) {
            System.out.print(z + " ");
        }
        System.out.println();
    }
    System.out.println("-------");
}

给定一个输入,我们可以使用printTable方法在每次迭代后检查这个表。例如,如果输入字符串是“dabcba”,那么最终的矩阵将如下所示:

1, 0, 0, 0, 0, 0

0, 1, 0, 0, 0, 1

0, 0, 1, 0, 1, 0

0, 0, 0, 1, 0, 0

0, 0, 0, 0, 1, 0

0, 0, 0, 0, 0, 1

从表中,我们可以清楚地看到,最长的字符串在单元格table[1] [5]中。

3. 更简单的算法

时间O(n2),空间O(1)。

public static String longestPalindrome3(String s) {
        if (s.isEmpty() || s.length() == 1) {
            return s;
        }
        String longest = s.substring(0, 1);
        for (int i = 0; i < s.length(); i++) {
            // 以一个字符为中心点的回文传
            String tmp = helper(s, i, i);
            if (tmp.length() > longest.length()) {
                longest = tmp;
            }
            // 以两个字符为中心点的回文传
            tmp = helper(s, i, i + 1);
            if (tmp.length() > longest.length()) {
                longest = tmp;
            }
        }
        return longest;
    }
    // 给一个中心点,一个或两个字符,找到以它为中心的最长回文子串
    public static String helper(String s, int begin, int end) {
        while (begin >= 0 && end <= s.length() - 1 && s.charAt(begin) == s.charAt(end)) {
            begin--;
            end++;
        }
        return s.substring(begin + 1, end);
    }

4. 测试代码

public static void main(String[] args) {
        String s = "abcdcbo";
        System.out.println(longestPalindrome1(s));

        String s2 = "dabcba";
        System.out.println(longestPalindrome2(s2));

        String s3 = "dabcba";
        System.out.println(longestPalindrome3(s3));
    }

测试结果:

bcdcb
1 0 0 0 0 0 
0 1 0 0 0 0 
0 0 1 0 0 0 
0 0 0 1 0 0 
0 0 0 0 1 0 
0 0 0 0 0 1 
-------
1 0 0 0 0 0 
0 1 0 0 0 0 
0 0 1 0 0 0 
0 0 0 1 0 0 
0 0 0 0 1 0 
0 0 0 0 0 1 
-------
1 0 0 0 0 0 
0 1 0 0 0 0 
0 0 1 0 1 0 
0 0 0 1 0 0 
0 0 0 0 1 0 
0 0 0 0 0 1 
-------
1 0 0 0 0 0 
0 1 0 0 0 1 
0 0 1 0 1 0 
0 0 0 1 0 0 
0 0 0 0 1 0 
0 0 0 0 0 1 
-------
abcba
abcba

进程已结束,退出代码 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值