【动态规划】LeetCode - 5. 最长回文子串

题目描述

题目链接
在这里插入图片描述

解法1:动态规划(是否回文用dp数组存储)

1. 定义状态

dp[i][j] 表示子串 s[i…j] 是否为回文子串,这里子串 s[i…j] 定义为左闭右闭区间,可以取到 s[i] 和 s[j]。

2. 状态转移方程

dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]

根据定义可知,j 要严格大于 i(字符串长度为1的不考虑,本题要找的是最长字符串),dp[i][i] = true(即对角线)
边界条件是:表达式 [i + 1, j - 1] 不构成区间,即长度严格小于 2,即 j - 1 - (i + 1) + 1 < 2 ,整理得 j - i < 3。

所以状态转移方程可以优化为:

dp[i][j] = (s[i] == s[j]) and (j - i < 3 or dp[i + 1][j - 1])

或者说,(s[i] == s[j]) and (j - i < 3 or dp[i + 1][j - 1]) 为 true 时, dp[i][j] = true

class Solution {
public:
    //不是列出所有情况,用不到DFS回溯
    string longestPalindrome(string s) {
        int len = s.size();
        if(len < 2) return s;
        vector<vector<bool> > dp(len, vector<bool>(len, false));
        // 状态:dp[i][j] 表示 s[i][j] 是否是回文
        // 状态转移方程:dp[i][j] = (s[i] == s[j]) and (j - i < 3 or dp[i + 1][j - 1])
        //初始化对角线为 true
        for(int i = 0; i < len; i++)
         dp[i][i] = true;
        int start = 0, max = 1;
        for (int right = 1; right < len; right++)
        {
            for (int left = 0; left < right; left++)
            {
                if(s[left] == s[right] && (right - left < 3 || dp[left + 1][right - 1]))
                {
                    dp[left][right] = true;
                    if(right - left + 1 > max)
                    {
                        max = right - left + 1;
                        start = left;
                    }
                } 
            }
        }
        return s.substr(start, max);//分割字符串substr(起始位置,分割长度)
    }
};

复杂度分析:

时间复杂度:O(N^2)
空间复杂度:O(N^2)

可以再练习一下 【回溯 + 动态规划】LeetCode - 131. 分割回文串 ,同为回文字符串的问题,所求的结果不同,解决的办法也就不同

解法2:中心扩散法判断是否回文(省去dp数组空间)

思路来源于 这里,为了方便日后学习,主要思想截图如下:
在这里插入图片描述
在这里插入图片描述
具体编码细节在以下的代码的注释中体现:

class Solution {
private:
    string centerSpread(string s, int left, int right) 
    {
        // left = right 的时候,此时回文中心是一个空隙,向两边扩散得到的回文子串的长度是奇数
        // right = left + 1 的时候,此时回文中心是一个字符,向两边扩散得到的回文子串的长度是偶数
        int n = s.size(), i =left, j = right;
        while(i >= 0 && j < n && s[i] == s[j])
        {
            i--; j++;
        }
        //跳出 while 循环时,恰好满足 s[i] != s[j],因此不能取 i,不能取 j
        return s.substr(i + 1, j - i - 1);
    }
public:
    //不是列出所有情况,用不到DFS回溯
    string longestPalindrome(string s) {
        int len = s.size();
        if(len < 2) return s;
        int maxLen = 1;
        string ans = s.substr(0, 1);//分割字符串substr(起始位置,分割长度)
        // 中心位置枚举到 len - 2 即可
        for (int i = 0; i < len - 1; i++)
        {
            string oddStr = centerSpread(s, i, i);
            string evenStr = centerSpread(s, i, i + 1);
            string maxLenStr = oddStr.size() > evenStr.size() ? oddStr : evenStr;
            if (maxLenStr.length() > maxLen) {
                maxLen = maxLenStr.size();
                ans = maxLenStr;
            }
        }
        return ans;
    }
};

复杂度分析:

时间复杂度:O(N^2)
空间复杂度:O(1),只使用到常数个临时变量,与字符串长度无关。

但是提交结果表明,虽然这样做的运行时间短了,但内存消耗大了。暂时还不理解为什么不用dp二维数组了,反而内存消耗变大了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值