动态规划-两个数组

在这里说明,我写的斐波那契数列模型是依托答辩,不建议看。这段时间比较懒,所以子数组,简单的多态,这几个会在最后写。

两个数组也是让我头脑发烫的难题。

1.最长公共子序列

class Solution {
public:
    int longestCommonSubsequence(string s, string t) {
        int m=s.size(),n=t.size();
        //以s[i]为结尾和以t[j]结尾,确定最长公共子序列(长度)
        //情况分类:dp[i][j]=s[i-1]==t[j-1]?dp[i-1][j-1]+1:max(dp[i-1][j],dp[i][j-1]);
        //当s[i-1]==t[j-1]时取dp[i-1][j-1]+1(+1表示增加1的长度),否则保留当前最大子序列长度
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));

        for(int i=1;i<=m;++i){
            for(int j=1;j<=n;++j){
                if(s[i-1]==t[j-1])
                dp[i][j]=dp[i-1][j-1]+1;
                else
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
        return dp[m][n];
    }
};

2. 最长重复子数组

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int m=nums1.size(),n=nums2.size();
        //该题与最长公共子序列基本相同
        //唯一区别在于nums1[i-1]!=nums2[j-1]时,dp[i][j]不需要保留当前最长子数组
        vector<vector<int>> dp(m+1,vector<int>(n+1));

        //小技巧:我们在nums头部插入一个数
        //我们就可以写if(nums1[i]==nums[j])且让i,j从1开始遍历
        int res=dp[0][0];
        for(int i=1;i<=m;++i){
            for(int j=1;j<=n;++j){
                if(nums1[i-1]==nums2[j-1])
                dp[i][j]=dp[i-1][j-1]+1;
                else
                dp[i][j]=0;
                res=max(dp[i][j],res);
            }
        }
        return res;
    }
};

如果心细的你还会发现(最长回文子序列)这题与1,2题相似。

3.不相交的线

class Solution {
public:
    int maxUncrossedLines(vector<int>& s, vector<int>& t) {
        int m=s.size(),n=t.size();
        //不相交==子序列,最大连线数==最长公共子序列
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        for(int i=1;i<=m;++i){
            for(int j=1;j<=n;++j){
                if(s[i-1]==t[j-1])
                dp[i][j]=dp[i-1][j-1]+1;
                else
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
        return dp[m][n];
    }
};

这些开胃小菜也就吃完了,我们开始真正的修行把。

4.不同的子序列

class Solution {
public:
    int numDistinct(string s, string t) {
        const int MOD=1e9+7;
        int m=t.size(),n=s.size();
        //在s[j]中有多少个t[i]子序列(状态表示)
        //状态转移方程:s[j-1]==t[i-1]->dp[i-1][j-1] 
        //继承dp[i][j-1]->如果出现s=dagg,t=dag,dp[i-1][j-1]完全不够。(这里建议看代码随想录)
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        for(int j=0;j<=n;++j)dp[0][j]=1;//当t.empty()=true,dp[0][j]=1

        for(int i=1;i<=m;++i){
            for(int j=1;j<=n;++j){
                dp[i][j]=dp[i][j-1];//不选s[j]
                if(s[j-1]==t[i-1])//符合条件就选
                dp[i][j]=(dp[i][j]+dp[i-1][j-1])%MOD;
            }
        }
        return dp[m][n];
    }
};

5.通配符匹配

class Solution {
public:
    bool isMatch(string s, string p) {
        int m=s.size(),n=p.size();
        s=' '+s,p=' '+p;//优化:不需要对p and s进行-1操作
        vector<vector<bool>> dp(m+1,vector<bool>(n+1));

        dp[0][0]=true;//当s.empty()==p.empty()==true
        for(int j=1;j<=n;++j)//*可以匹配空字符
        if(p[j]=='*')dp[0][j]=true;
        else break;

        //这里可以分为*与非*两种情况
        //1.非*:当p[j]为字母时:p[j]==s[i]&&dp[i-1][j-1]才能匹配,当p[j]=='?'只需要判断dp[i-1][j-1]
        //2.*:分为*代表空,代表1个字符,代表2个字符,3个字符.....n个字符
        //空时:判断s:0~i是否与p:0~j-1相等->dp[i][j-1];
        //1个:dp[i-1][j-1]   2个:dp[i-2][j-1]  3个:dp[i-3][j-1]....n个:dp[i-n][j-1]
        //如果我们再写一个for循环来表示*情况,时间复杂度就是O(n^3)可能超时,所以我们必须优化为O(1)
        //dp[i][j]=dp[i][j-1]||dp[i-1][j-1]||dp[i-2][j-1]||dp[i-3][j-1]||....dp[i-n][j-1]
        //可以推导出dp[i-1][j]=dp[i-1][j-1]||dp[i-2][j-1]||dp[i-3][j-1]||....dp[i-n][j-1]
        //由此可知dp[i][j]=dp[i][j-1]||dp[i-1][j]
        for(int i=1;i<=m;++i){
            for(int j=1;j<=n;++j){
                if(p[j]=='*')
                dp[i][j]=dp[i-1][j]||dp[i][j-1];
                else
                dp[i][j]=dp[i-1][j-1]&&(s[i]==p[j]||p[j]=='?');
            }
        }
        return dp[m][n];
    }
};

这题非常有意思对吧。

6.正则表达式匹配

class Solution {
public:
    bool isMatch(string s, string p) {
        int m=s.size(),n=p.size();
        s=' '+s,p=' '+p;
        //这题的唯一区别在于*与前一个字符绑定了,也就是说*想表示空,会连同前一个字符一起删除
        vector<vector<bool>> dp(m+1,vector<bool>(n+1,false));

        dp[0][0]=true;
        for(int j=2;j<=n;j+=2)//如果连续的间隔的存在*,就可以表示空与s为空时匹配
        if(p[j]=='*')dp[0][j]=true;
        else break;

        //p[j]=='*'&&p[j-1]为字母时分为两种情况:空 or n个
        //空:s:0~i与p:0~j-2相同时true,也就是dp[i][j-2]
        //n个:当s[i]==p[j-1]&&dp[i-1][j-2]||s[i-1]==p[j-1]&&dp[i-2][j-2]...s[i-n-1]==p[j-1]&&dp[i-n][j-2]
        //这不就是(通配符匹配)中的dp[i-1][j]再ands[i]==p[j-1]即可
        for(int i=1;i<=m;++i){
            for(int j=1;j<=n;++j){
                if(p[j]=='*'){
                    if(p[j-1]=='.')//这里与(通配符匹配)道理相同,只不过这次是 ?+*
                    dp[i][j]=dp[i-1][j]||dp[i][j-2];
                    else
                    dp[i][j]=dp[i][j-2]||(dp[i-1][j]&&s[i]==p[j-1]);
                }
                else
                dp[i][j]=dp[i-1][j-1]&&(s[i]==p[j]||p[j]=='.');
            }
        }
        return dp[m][n];
    }
};

如果你还意犹未尽可以娱乐一下这两题:交错字符串 and 两个字符串的最小ASCII删除和.

结束....

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值