最长回文串----动态规划专题

一、题目信息

1. 题目描述

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

2. 示例1

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

3. 示例2

输入: “cbbd”
输出: “bb”

4. 题目来源

来自 LeetCode 第5题----最长回文子串

二、题目解析

1. 动态规划

1.1 思路

拿到一道题,最先想到的办法就是“暴力求解”了,简单粗暴,但是这种方法通常时间复杂度比较高,像这道题,如果是用“暴力求解”,选出所有子字符串可能的开始和结束位置,并检验它是不是回文,然后选出最长的回文串。这样做的时间复杂度是O(n)。解法没有问题,但是有可能 timeout limit

我们考虑动态规划来优化“暴力法”。
string longestPalindrome(string s)
假设 s 长度为 n,则开辟一个 n2 大小的二维数组
dp[n][n],二维数组可以是动态开辟,但是这道题说字符串的长度不会超过1000,我们直接开辟了 dp[1001][1001]dp[j][i]表示s[j]s[i]所表示的子串是否为回文串,是则为1,否则为0。
会有两种情况:
(1)s[j] == s[i],只要s[j+1]s[i-1]是回文串,s[j]s[i]就是回文串,如果s[j+1]s[i-1]不是回文串,则s[j]s[i]也不是回文串;
(2)s[j] != s[i],那么s[j]s[i]一定不是回文串。

1.2 状态转移方程

d p [ j ] [ i ] = { d p [ j + 1 ] [ i − 1 ] , s[j] == s[i] 0 , s[j] != s[i] dp[j][i] = \begin{cases} dp[j+1][i-1], & \text{s[j] == s[i]} \\ 0, & \text{s[j] != s[i]} \end{cases} dp[j][i]={dp[j+1][i1],0,s[j] == s[i]s[j] != s[i]

1.3 复杂度分析

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

1.4 代码实现
class Solution {
public:
    string longestPalindrome(string s) {
        // 我们考虑动态规划
        // dp[s.size()][s.size()]
        // dp[i][j] == true 就是 si...sj是回文子串
        // 状态转移方程: p(i,j) = (p(i+1, j-1) && si == sj)
        int n = static_cast<int>(s.size());
        if(n < 1)
        {
            return "";
        }
        // 记录最长回文串长度
        int max_len = 0;
        // 记录回文串开始的下标
        int start = 0;
        // 状态数组
        int dp[1001][1001] = {0};
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j <= i; ++j)
            {
                // 如果 i,j 相邻或相等,直接判断s[i]与s[j]是否相等
                if(i-j < 2)
                {
                    dp[j][i] = (s[j] == s[i]);
                }
                else
                {
                    dp[j][i] = (dp[j+1][i-1] && (s[i] == s[j]));
                }
                
                if(dp[j][i] && max_len < i-j+1)
                {
                    max_len = i-j+1;
                    start = j;
                }
            }
        }
        
        return s.substr(start, max_len);
    }
};

2. 中心扩展算法

2.1 思路

如果一段字符串是回文串,那么以某个字符为中心的前缀和后缀必定是相同的。例如,以回文串"aba"为例,以b为中心,它的前缀和后缀都是a。因此,我们可以枚举中心位置,然后再在该位置上扩展,记录并更新得到的最长回文串长度。

2.2 注意事项

需要分别考虑字符串长度为奇数和偶数的情况。

2.3 复杂度分析

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)

2.4 代码实现
class Solution {
public:
    string longestPalindrome(string s) {
        // 中心扩展法
        // 如果一个字符串是回文串,那么以某个字符为中心的前缀和后缀必然相同
        // 例如回文串 "aba" 以 b 为中心,前后缀都是 a
        int n = static_cast<int>(s.size());
        if(n < 1)
        {
            return "";
        }
        int max_len = 0;
        int start = 0;
        int tmp = 0;
        
        // i 为回文中心位置
        for(int i = 0; i < n; ++i)
        {
            // 回文长度为奇数
            for(int j = 0; ((i-j >= 0) && (i+j < n)); ++j)
            {
                if(s[i-j] != s[i+j])
                    break;
                tmp = j * 2 + 1;
            }
            if(tmp > max_len)
            {
                max_len = tmp;
                start = i - max_len/2;
            }
            
            // 回文长度为偶数
            for(int j = 0; ((i-j >= 0) && (i+j+1 < n)); ++j)
            {
                if(s[i-j] != s[i+j+1])
                {
                    break;
                }
                tmp = j * 2 + 2;
            }
            if(tmp > max_len)
            {
                max_len = tmp;
                start = i - max_len/2 + 1;
            }
        }
        
        return s.substr(start, max_len);
    }
};

3. Manacher算法

// TODO

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值