【LeetCode】5. Longest Palindromic Substring

来源:互联网 发布:java中的double类型 编辑:程序博客网 时间:2024/05/16 14:13

算法小白,最近刷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:Input: "babad"Output: "bab"

Note: “aba” is also a valid answer.

Example:Input: "cbbd"Output: "bb"

题目让找出最长的回文子串。这道题一开始只能想到枚举所有区间,再进行回文判断的方式,这样的方法肯定是会超时的,最后看了题解,说是要用动态规划。

本题的动规思路是,如果区间[i, j] (i<=j) 构成一个回文串,那么区间[i-1, j+1]构成回文串的充要条件是 str[i-1]==str[j+1], 因此基本的递推公式为:
dp[i, j] = dp[i+1, j-1] && str[i+1] == str[j-1]
dp[i, j]是bool型,表示区间 [i, j] 构成的子串是否是个回文串,显然这里 i<=j
:这里使用的是左闭右闭区间,C++规范一般要求使用左闭右开区间

有了递推公式,接下来就得考虑初值了,很显然,dp[i, i] (i=0…n)是等于true的,但只有这一类的初值明显不够,无法完成递推计算,还需要d[i, i+1] 这类的初值,显然,d[i, i+1] = (str[i] == str[i+1])

OK, 递推公式和初值都有了,接下来就可以码代码了。

class Solution {public:    bool dp[1000+5][1000+5];    string longestPalindrome(string s) {        int len = s.length();        if (len == 1) return s;        if (len == 2) {            if (s[0] == s[1]) return s;            else return s.substr(0, 1);        } // 特殊情况直接返回        int beg = 0, end = 0;        int maxlen = 1;        memset(dp, false, sizeof(dp));        // 生成初值,即dp[i,i]和dp[i,i+1]        for (int i = 0; i < len; ++i){            dp[i][i] = true;            if (i+1 < len) {                dp[i][i+1] = (s[i] == s[i+1]);                if (dp[i][i+1]) { beg = i; end = i + 1; maxlen = 2; }            }        }        // 利用递推公式,同时更新最大长度和对应的区间下标        for (int i = 2; i < len; ++i){            for (int j = 0; j+i < len; ++j){                dp[j][j+i] = ( dp[j+1][j+i-1] && (s[j] == s[j+i]) );                if (dp[j][j+i] && i+1 > maxlen){                    beg = j; end = j + i; maxlen = i+1;                }            }        }        return s.substr(beg, maxlen);    }};

将dp看做一个方阵,从递推公式中,我们可以观察到,dp[i, j] 的值只与其左下角的值dp[i+1, j-1] 有关,因此,从空间上来说,并没有必要记录所有的 i, j 组合,在更新时完全可以覆盖后续不再用到的空间,因此,只需要一个两行的数组就可以满足要求。

另一点要注意的时,由于更新时要使用左下角即靠前的值,因此要进行倒序更新,代码如下:

class Solution {public:    bool dp[2][1000+5]; // 使用两行就够了    string longestPalindrome(string s) {        int len = s.length();        if (len == 1) return s;        if (len == 2) {            if (s[0] == s[1]) return s;            else return s.substr(0, 1);        }        int beg = 0, end = 0;        int maxlen = 1;        memset(dp, true, sizeof(dp)); // 主要是给第一行赋初值,即dp[i][i],全部为true        // 第二行进行手动更新,对应之前代码中的dp[i, i+1]        for (int i = len - 1; i - 1 >= 0; --i){            dp[1][i] = (s[i] == s[i-1]);            if (dp[1][i]) {                beg = i-1; end = i; maxlen = 2;             }        }        for (int i = 2; i < len; ++i){            for (int j = len - 1; j - i >= 0; --j) { // 进行倒序更新,覆盖不再用到的值                dp[i%2][j] = (dp[(i%2)][j-1] && (s[j] == s[j-i]));                 if (dp[i%2][j] && i + 1 > maxlen){                    beg = j - i; end = j; maxlen = i+1; }            }        }        return s.substr(beg, maxlen);    }};

上述方法的时间复杂度都是O(n^2),提交后也只是打败了20%的小伙伴。

还有一种中心扩展的方法,不过他的时间复杂度也是O(n^2),这里先不介绍了,题解里面说,还有一种O(n)的牛X方法,叫做Manacher算法,等以后有机会再慢慢研究

0 0
原创粉丝点击