leetcode-最长回文子串

 题目来自LeetCode,链接:最长回文子串。给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

 示例1:

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

 示例2:

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

 首先最简单的就是暴力法了,比较简单就不多讲了,注意从长度较长往较短的方向遍历就好了。时间复杂度是 O ( n 3 ) O(n^{3}) O(n3),空间复杂度这里因为把字符串转为了数组所以是 O ( n ) O(n) O(n),当然不转直接用charAt()去获取各位置上的字符也是可以的,空间复杂度就是 O ( 1 ) O(1) O(1)了。

 JAVA版代码如下:

class Solution {
    public String longestPalindrome(String s) {
        if (s.length() == 0) {
            return "";
        }
        char[] chars = s.toCharArray();
        for (int i = chars.length; i > 1; --i) {
            for (int j = 0; j <= chars.length - i; ++j) {
                boolean match = true;
                for (int k = 0; k < i / 2; ++k) {
                    if (chars[j + k] != chars[j + i - k - 1]) {
                        match = false;
                        break;
                    }
                }
                if (match) {
                    return new String(chars, j, i);
                }
            }
        }
        return new String(chars, 0, 1);
    }
}

 提交结果如下:


 接着再看怎么进一步减少时间复杂度,考虑到一个字符串s[i:j]为回文串的条件是s[i]==s[j]s[i+1:j-1]也为回文串,所以可以用动态规划来做。定义dp[i][j]表示字符串从ij这一段是否为回文串,那么dp[i][j] = s[i]==s[j] && (j-1<i+1 || dp[i+1][j-1]),这里j-1<i+1的意思是字符串的长度为1或2的情况下只要s[i]==s[j]就是回文串,否则就需要dp[i+1][j-1]=true。这种方法的时间复杂度为 O ( n 2 ) O(n^{2}) O(n2),空间复杂度为 O ( n 2 ) O(n^{2}) O(n2)

 JAVA版代码如下:

class Solution {
    public String longestPalindrome(String s) {
        if (s.length() == 0) {
            return "";
        }
        char[] chars = s.toCharArray();
        int N = chars.length;
        // dp[i][j]表示从i到j这一段是回文串
        boolean[][] dp = new boolean[N][N];
        int left = 0, maxLen = 0;
        for (int i = N - 1; i >= 0; --i) {
            for (int j = i; j < N; ++j) {
                dp[i][j] = (chars[i] == chars[j]) && (j - 1 < i + 1 || dp[i + 1][j - 1]);
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    left = i;
                }
            }
        }
        return new String(chars, left, maxLen);
    }
}

 提交结果如下:


 同样,每次这种动态规划的题目看到状态只与前几个状态有关的我们总是可以进一步减少空间复杂度,这里可以只用一个长度为n的数组配合一个变量来保存状态,将空间复杂度降到 O ( n ) O(n) O(n)

 JAVA版代码如下:

class Solution {
    public String longestPalindrome(String s) {
        if (s.length() == 0) {
            return "";
        }
        char[] chars = s.toCharArray();
        int N = chars.length;
        // dp[i][j]表示从i到j这一段是回文串
        boolean[] dp = new boolean[N];
        int left = 0, maxLen = 0;
        for (int i = N - 1; i >= 0; --i) {
            boolean dp_i_1 = true;
            for (int j = i; j < N; ++j) {
                boolean temp = dp[j];
                dp[j] = (chars[i] == chars[j]) && (j - 1 < i + 1 || dp_i_1);
                dp_i_1 = temp;
                if (dp[j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    left = i;
                }
            }
        }
        return new String(chars, left, maxLen);
    }
}

 提交结果如下:


 接着这里再用另一种思路,找回文串的时候是从字符串中的某个点(这个点可以是某个字符比如aba中的b或者某两个字符之间的空白比如abbabb之间的空白作为出发点)出发,往两边延伸找到以当前点为中心的最大回文串。这种方法的时间复杂度还是 O ( n 2 ) O(n^{2}) O(n2),空间复杂度看实现可能有所不同,比如下面的代码因为把字符串保存为字符数组以加速所以空间复杂度是 O ( n ) O(n) O(n),但如果用charAt()就可以达到 O ( 1 ) O(1) O(1)的空间复杂度。

 JAVA版代码如下:

class Solution {
    private char[] chars;
    private int maxLength(int left, int right) {
        while (left >= 0 && right < chars.length && chars[left] == chars[right]) {
            --left;
            ++right;
        }
        return right - left - 1;
    }
    public String longestPalindrome(String s) {
        if (s.length() == 0) {
            return "";
        }
        chars = s.toCharArray();
        int middleIdx = 0, maxLen = 0;
        for (int i = 0; i < chars.length; ++i) {
            int longer = Math.max(maxLength(i, i), maxLength(i, i + 1));
            if (longer > maxLen) {
                maxLen = longer;
                middleIdx = i;
            }
        }
        int startIdx = maxLen % 2 == 0 ? middleIdx - maxLen / 2 + 1 : middleIdx - maxLen / 2;
        
        return new String(chars, startIdx, maxLen);
    }
}

 提交结果如下:


 最后介绍一下时间复杂度和空间复杂度均为 O ( n ) O(n) O(n)的马拉车算法(Manacher’s Algorithm),具体算法介绍可以看这里

 JAVA版代码如下:

class Solution {
    
    public String longestPalindrome(String s) {
        int L = s.length();
        if (L == 0) {
            return "";
        }
        char[] chars = new char[L * 2 + 3];
        int[] P = new int[L * 2 + 3];
        chars[0] = '^';
        chars[chars.length - 1] = '$';
        chars[chars.length - 2] = '#';
        for (int i = 0; i < L; ++i) {
            chars[2 * i + 1] = '#';
            chars[2 * i + 2] = s.charAt(i);
        }
        int center = 0, right = 0;
        int maxIdx = 0, maxLen = 0;
        for (int i = 1; i < chars.length - 1; ++i) {
            if (i < right) {
                P[i] = Math.min(P[2 * center - i], right - i);
            }
            else {
                P[i] = 0;
            }

            while (chars[i + P[i] + 1] == chars[i - P[i] - 1]) {
                ++P[i];
            }

            if (i + P[i] > right) {
                right = i + P[i];
                center = i;
            }

            if (P[i] > maxLen) {
                maxLen = P[i];
                maxIdx = i;
            }
        }
        int startIdx = (maxIdx - P[maxIdx] - 1) / 2;
        return s.substring(startIdx, startIdx + maxLen);
    }
}

 提交结果如下:


 Python版代码如下:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        extendStr = '^'
        for ss in s:
            extendStr += '#' + ss
        extendStr += '#$'

        P = [0 for _ in range(len(extendStr))]
        center, right = 0, 0
        maxIdx, maxLen = 0, 0

        for i in range(1, len(extendStr) - 1):
            if i < right:
                P[i] = min(P[2 * center - i], right - i)
            else:
                P[i] = 0
            
            while extendStr[i - P[i] - 1] == extendStr[i + P[i] + 1]:
                P[i] += 1

            if i + P[i] > right:
                right = i + P[i]
                center = i

            if P[i] > maxLen:
                maxLen = P[i]
                maxIdx = i
        
        startIdx = (maxIdx - P[maxIdx] - 1) // 2
        return s[startIdx : startIdx + maxLen]

 提交结果如下:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值