【leetcode】最长回文子串(区间dp)

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

示例 1:

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

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

链接:leetcode原题

思路分析:

这题是区间dp经典题目。
先来看看回文子串的定义:
长度为1:a, b. 单个字符就是一个回文串
长度为2:aa, bb. 两个相同的字符就是一个回文串
长度为3:aba, dad.
长度为4:abba, daad.
长度为5:abcba, dacad.
我们可以定义两个状态i, j表示区间[i,j]. 令dp[i][j]表示区间[i,j]是否是回文子串。
可以发现,dp初始状态在长度为1,2的条件。当长度>2时,若s[i] == s[j],则dp[i][j] = dp[i+1][j-1]. 也就是说,只要s[i] == s[j],并且区间[i+1,j-1]是回文串,那么区间[i,j]就是回文串。比如aba,s[0] == s[2] == ‘a’,中间的b(dp[1][1])是回文串,于是aba是回文串。又比如abba,s[0] == s[3],dp[1][2]是回文串,那么abba就是回文串。
那么我们就可以验证该状态转移方程是正确的了。

接下来就是写递推。我们需要初始化长度1,长度2的回文串,长度3的回文串由长度1推出,长度4的回文串由长度2推出,长度5的回文串由长度3推出…
于是第一层for循环是长度[3,n]递推。
那么第二层for循环便是遍历区间的起点。
区间的终点 = 区间起点+区间长度-1.
两层for循环,时间复杂度O(n^2).
由于长度是递增的递推,因此我们只需要在满足回文串的条件下更新最大长度maxLen,就可以得到最长的回文子串长度。
下面上代码:

class Solution {
public:
    int dp[1010][1010];
    string longestPalindrome(string s) {
        memset(dp,0,sizeof(dp));    
        int n = s.length();
        int l = 0, maxLen = 1;
        for(int i = 0;i < s.length();i++)	//初始化
        {
            dp[i][i] = 1;	//长度为1,自身就是回文串
            if(s[i] == s[i+1] && i+1 < n)	//两个相同字符组成长度为2的回文串
            dp[i][i+1] = 2, l = i, maxLen = 2;
        }
        
        for(int len = 3;len <= n;len++)	//区间长度递推
        {
            for(int i = 0;i+len-1 < n;i++)	//区间起点枚举
            {
                int j = i+len-1;	//区间终点
                if(s[i] == s[j] && dp[i+1][j-1])	//满足回文串条件
                {
                    dp[i][j] = 1;	//定义区间[i,j]为回文串
                    l = i, maxLen = len;	//更新区间起点和长度
                }
            }
        }
        return s.substr(l,maxLen);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值