LeetCode—动态规划

T5 最长回文子串

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

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

示例 2:
输入: “cbbd”
输出: “bb”

传统的验证回文串的方法就是两个两个的对称验证是否相等,那么对于找回文字串的问题,就要以每一个字符为中心,像两边扩散来寻找回文串,这个算法的时间复杂度是O(n*n)

要注意奇偶情况,由于回文串的长度可奇可偶,比如”bob”是奇数形式的回文,”noon”就是偶数形式的回文,两种形式的回文都要搜索

动态规划

我们维护一个二维数组dp,其中dp[i][j]表示字符串区间[i, j]是否为回文串,
(1)当i = j时,只有一个字符,肯定是回文串,
(2)如果i = j + 1,说明是相邻字符,此时需要判断s[i]是否等于s[j],
(3)如果i和j不相邻,即i - j >= 2时,除了判断s[i]和s[j]相等之外,
dp[j + 1][i - 1]若为真,就是回文串,通过以上分析,可以写出递推式如下:

dp[i,j] = 1                                   if i == j
dp[i,j] = s[i] == s[j]                        if j = i + 1
dp[i,j] = s[i] == s[j] && dp[i + 1][j - 1]    if j > i + 1  

这里有个有趣的现象就是如果我把下面的代码中的二维数组由int改为
vector< vector < int> >后,就会超时,这说明int型的二维数组访问执行速度完爆std的vector啊,所以以后尽可能的还是用最原始的数据类型吧。

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        int dp[n][n] = {0};
        int left=0, right=0, len=0;

        for(int i=0; i<n; i++)
        {
            dp[i][i] = 1;   // 一个字符肯定是回文

            for(int j=0; j<i; j++)
            {
                dp[j][i] = {s[j]==s[i] && (i-j<2 || dp[j+1][i-1])};   // 动态规划,前一个字串也是回文串

                if(dp[j][i] && len<i-j+1)  // 之前回文串短于当前回文串
                {
                    len = i-j+1;
                    left = j;
                    right = i;
                }
            }
        }
        return s.substr(left, right-left+1);
    }
};

T96 不同的二叉搜索树

给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?

这道题实际上是卡塔兰数的不像斐波那契数那样人尽皆知

我们先来看当 n = 1的情况,只能形成唯一的一棵二叉搜索树,n分别为1,2,3的情况如下所示:
这里写图片描述

这里写图片描述

class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n+1,0);
        dp[0]=1;
        dp[1]=1;

        for(int i=2; i<=n; i++)
        {
            for(int j=0; j<i; j++)
                dp[i] = dp[i] + dp[j]*dp[i-j-1];
        }
        return dp[n];
    }
};

T392 判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

示例 1:
s = “abc”, t = “ahbgdc”
返回 true.

示例 2:
s = “axc”, t = “ahbgdc”
返回 false.

这道题算比较简单的一种,我们可以用两个指针分别指向字符串s和t,然后如果字符相等,则i和j自增1,反之只有j自增1,最后看i是否等于s的长度,等于说明s已经遍历完了,而且字符都有在t中出现过

class Solution {
public:
    bool isSubsequence(string s, string t) {
        if(s.empty()) return true;
        int i=0,j=0;

        while(i<s.size() && j<t.size())
        {
            if(s[i]==t[j])
            {
                ++i;
                ++j;
            }
            else
                ++j;
        }
        return i==s.size();
    }
};

T647 回文字串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。

示例 1:
输入: “abc”
输出: 3
解释: 三个回文子串: “a”, “b”, “c”.

示例 2:
输入: “aaa”
输出: 6
说明: 6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”.

需要一个二维数组来记录子字符串[i, j]是否是回文串,那么我们直接就将dp[i][j]定义成子字符串[i, j]是否是回文串就行了,然后我们i从n-1往0遍历,j从i往n-1遍历,然后我们看s[i]和s[j]是否相等,这时候我们需要留意一下,有了s[i]和s[j]相等这个条件后,i和j的位置关系很重要,如果i和j相等了,那么dp[i][j]肯定是true;如果i和j是相邻的,那么dp[i][j]也是true;如果i和j中间只有一个字符,那么dp[i][j]还是true;如果中间有多余一个字符存在,那么我们需要看dp[i+1][j-1]是否为true,若为true,那么dp[i][j]就是true。赋值dp[i][j]后,如果其为true,结果res自增1,参见代码如下:

class Solution {
public:
    int countSubstrings(string s) {
        int n=s.size();
        int res=0;
        vector<vector<bool>> dp(n,vector<bool>(n,false));

        for(int i=n-1; i>=0; i--)
        {
            for(int j=i; j<n; j++)
            {
                dp[i][j] = (s[i]==s[j]) && (j-i<=2 || dp[i+1][j-1]);
                if(dp[i][j]) 
                    ++res;
            }
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值