LeetCode 5. Longest Palindromic Substring

题目

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"
Output: "bb"

某a公司OA题库之一,要求一个string里最长的回文序列。

LC的solutions里给出了几种解法,第一种是先把它倒过来,称作s‘,然后采用longest common substring的dp解法判断s和s‘之间的lcs,但是要注意check这个substring的起始位置是否相同,因为可能会出现倒过来和正过来一样但并不是回文序列的情况。

感觉这道题的主流解法有以下两种:

1. 动态规划

记P(i, j)为以i开头以j结尾的substring是否为回文序列,那么我们可以得出:P(i, i)=true,P(i, i+1)=(S[i] == s[i + 1])。对于任意P(i, j)来说,P(i, j) = P(i + 1, j - 1) && S[i] == P[j]。在这里需要注意的是,由于我们的i <= j,因此dp的二维矩阵只需要考虑对角线的右上部分,并且由于base case已经考虑了(i, i)和(i, i+1)的情况,因此矩阵的最后一行和倒数第二行也不需要进行判断,从倒数第三行开始就可以了,遍历的顺序由于P(i, j)是基于P(i + 1, j - 1)的,所以i从倒数第三行开始往前遍历,j正着从i+2开始遍历。写代码的时候还有一个小坑,就是初始化数组的时候虽然写了={false},但是并不会直接写成全是false的二维数组,所以还得手动再写一次,不然后面遍历的时候可能会出现数组元素未被初始化的错误。代码的时间复杂度和空间复杂度都是O(n^2),运行时间184ms,24.14%,空间10M,48.96%:

class Solution {
public:
    string longestPalindrome(string s) {
        int size = s.size();
        if (size == 0) {
            return "";
        }
        bool dp[size][size] = {false};
        for (int i = 0; i < size; i++) {
            for (int j = i; j < size; j++) {
                dp[i][j] = false;
            }
            dp[i][i] = true;
            if (i + 1 < size && s[i] == s[i + 1]) {
                dp[i][i + 1] = true;
            }
        }
        for (int i = size - 3; i >= 0; i--) {
            for (int j = i + 2; j < size; j++) {
                if ((s[i] == s[j]) && dp[i + 1][j - 1]) {
                    dp[i][j] = true;
                }
                else {
                    dp[i][j] = false;
                }
            }
        }
        int max_index = 0;
        int max_length = 0;
        for (int i = 0; i < size; i++) {
            for (int j = i; j < size; j++) {
                if (dp[i][j]) {
                    if (j - i + 1 > max_length) {
                        max_length = j - i + 1;
                        max_index = i;
                    }
                }
            }
        }
        return s.substr(max_index, max_length);
    }
};

2. 从中心到两边的扩展方法:

由于回文序列相当于从中间往两边走是一样的,因此我们可以对字符串进行遍历,看以某一个字符为中心能够扩展出多长的回文序列。我们可以写一个helper function用来check从两个中心开始分别往两边找,能找出多长的回文序列(要从两个中心开始的原因是,回文序列可能是以一个元素为中心,此时两个中心重合,也可能是以两个元素为中心)。在遍历字符串的时候,对每个字符都执行一次以它自己为中心、一次以它和它后面的元素为中心的两次查找,最后取所有里面的最大值即可。需要注意的是,由于我们传入的是两个中心的index,所以最后返回来时计算回文序列的index需要仔细留意一下。运行时间32ms,62.38%,空间103.9M,17.24,这个空间使用率真是不堪入目。

class Solution {
public:
    string longestPalindrome(string s) {
        if (s.size() == 0) {
            return "";
        }
        int max_len = 0;
        int max_index = 0;
        for (int i = 0; i < s.size(); i++) {
            int len1 = palindromeLen(s, i, i);
            int len2 = palindromeLen(s, i, i + 1);
            int len = max(len1, len2);
            if (len > max_len) {
                max_len = len;
                max_index = i - (len - 1) / 2;
            }
        }
        return s.substr(max_index, max_len);
    }
    
    int palindromeLen(string s, int l, int r) {
        while (l >= 0 && r < s.size() && s[l] == s[r]) {
            l--;
            r++;
        }
        return r - l - 1;
    }
};

在expand(从中心向外延伸,也就是我的计算len的函数之前),如果可以把重复的字符跳过,将会大大提升运行效率,像这个:https://leetcode.com/problems/longest-palindromic-substring/discuss/2929/Accepted-4ms-c%2B%2B-solution.

还有一些其他的做法,暂时先不看了,贴在这里:https://leetcode.com/problems/longest-palindromic-substring/discuss/3060/(AC)-relatively-short-and-very-clear-Java-solution


2020.10.10 Java

前两天面试遇到这道题,还好前阵子复习了一下这道题的解法,就直接上了。刚把面试时写的代码放lc上运行了下,除了一些小细节问题以外居然直接就pass了,菜鸡震惊脸.jpg 时间复杂度O(n^2)。直接把代码丢上来:

Runtime: 29 ms, faster than 56.67% of Java online submissions for Longest Palindromic Substring.

Memory Usage: 38.9 MB, less than 5.02% of Java online submissions for Longest Palindromic Substring.

class Solution {
    public String longestPalindrome(String s) {
        int longestLength = 0;
        String longestString = "";
        for (int i = 0; i < s.length(); i++) {
            String curr1 = longestPalindrome(s, i, i);
            String curr2 = longestPalindrome(s, i, i + 1);
            if (curr1.length() > longestLength) {
                longestLength = curr1.length();
                longestString = curr1;
            }
            if (curr2.length() > longestLength) {
                longestLength = curr2.length();
                longestString = curr2;
            }
        }
        return longestString;
    }
    
    private String longestPalindrome(String s, int index1, int index2) {
        int left = index1;
        int right = index2;
        while (left >= 0 && right < s.length()) {
            if (s.charAt(left) != s.charAt(right)) {
                break;
            }
            left--;
            right++;
        }
        return s.substring(left + 1, right);
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值