动态规划算法专题七--两个数组dp问题

目录

题四十 最长公共子序列

 题四十一 不相交的线

 题四十二 不同子序列

 题四十三 通配符匹配

  题四十四 正则表达式匹配

  题四十五 交错字符串

  题四十六 两个字符串的最小ASCII删除和

 题四十七 交最长重复子数组

题四十 最长公共子序列

1143. 最长公共子序列 - 力扣(LeetCode)

题解:

状态方程:dp[i][j]:表示s1字符串[0,i]区间和s2字符串[0,j]区间,最长公共子序列。

根据方程进行推导:分两种情况讨论当s1[i] == s2[j] / s1[i] != s2[j]

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        //1、创建dp表
        int n = text1.size();
        int m = text2.size();
        vector<vector<int>> dp(n+1, vector<int>(m+1));

        //2、初始化
        text1 = " " + text1;
        text2 = " " + text2;

        //3、填表
        for(int i = 1; i<n+1; ++i)
        {
            for(int j = 1; j<m+1; ++j)
            {
                if(text1[i] == text2[j])
                {
                    dp[i][j] = dp[i-1][j-1] + 1;
                }
                else
                {
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }

        //返回值
        return dp[n][m];

    }
};

 题四十一 不相交的线

1035. 不相交的线 - 力扣(LeetCode)

题解:

状态方程:dp[i][j]:nums1序列[0,i]区间和nums2序列[0,j]区间,有多少个不相交线。

根据方程进行推导:分两种情况讨论当s1[i] == s2[j] / s1[i] != s2[j]

class Solution {
public:
    int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
        //1、创建dp表
        int m = nums1.size();
        int n = nums2.size();
        vector<vector<int>> dp(m+1, vector<int> (n+1));

        //2、初始化

        //3、填表
        for(int i = 1; i<m+1; ++i)
        {
            for(int j = 1; j<n+1; ++j)
            {
                if(nums1[i-1] == nums2[j-1])
                {
                    dp[i][j] = dp[i-1][j-1] + 1;
                }
                else
                {
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }

        //4、返回值
        return dp[m][n];




    }
};    



 题四十二 不同子序列

LCR 097. 不同的子序列 - 力扣(LeetCode)

题解:

dp[i][j]:表示[0,i]区间的s串,有多少个[0,j]区间的t串。

根据此状态方程推导。

class Solution {
public:
    int numDistinct(string s, string t) {
        //1、创建dp表
        int m = s.size();
        int n = t. size();
        vector<vector<double>> dp(m+1, vector<double> (n+1));

        //2、初始化
        for(int i = 0; i<m+1; ++i)
        {
            dp[i][0] = 1;
        }

        //3、填表
        for(int i = 1; i<m+1; ++i)
        {
            for(int j = 1; j<n+1; ++j)
            {
                dp[i][j] += dp[i-1][j];
                if(s[i-1] == t[j-1])
                {
                    dp[i][j] += dp[i-1][j-1];
                }
            }
        }

        //4、返回值
        return dp[m][n];
        
    }
};

 题四十三 通配符匹配

44. 通配符匹配 - 力扣(LeetCode)

题解:

?问号可以匹配一个字母
*星号可以匹配任意
s是原字符串,p是带有符号的字符串,问题是带有符号的p串能否匹配原字符串s

dp[i][j]:[0,j]的p串,能否匹配[0,i]的s字串。

根据此状态方程推导,分类情况讨论,分别讨论当d[ j ]是?、*、普通字符时的各种情况。

class Solution {
public:
    bool isMatch(string s, string p) {
        //1、创建dp表
        int m = s.size();
        int n = p.size();
        vector<vector<bool>> dp(m+1, vector<bool>(n+1));
    
        //2、初始化
        s = " " + s;
        p = " " + p;
        //初始化第一列
        //此时p是一个空串,只有s也是空串才能匹配
        dp[0][0] = true;

        //初始化第一行
        //此时s是空串,p只要不是*就是false
        for(int j = 1; j<=n; ++j)
        {
            if(p[j] == '*')
                dp[0][j] = true;
            else 
                break;
        }

        //3、填表
        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] = (s[i] == p[j] || p[j] == '?') && dp[i-1][j-1];
                }
            }
        }

        //4、返回值
        return dp[m][n];


    }
};

  题四十四 正则表达式匹配

10. 正则表达式匹配 - 力扣(LeetCode)

题解:

?问号可以匹配一个字母
*星号和前面一个符号,可以匹配任意
s是原字符串,p是带有符号的字符串,问题是带有符号的p串能否匹配原字符串s

dp[i][j]:[0,j]的p串,能否匹配[0,i]的s字串。
分情况讨论:
看p的最后一个位置:
1、当p[j]为*号
1)p[j-1] 为 . 号
2)p[j-1]为普通字母

2、当p[j]为普通字母/ .号

class Solution {
public:
    bool isMatch(string s, string p) {
      //1、创建dp表
        int m = s.size();
        int n = p.size();
        vector<vector<bool>> dp(m+1, vector<bool>(n+1));
    
        //2、初始化
        s = " " + s;
        p = " " + p;
        //初始化第一列
        //此时p是一个空串,只有s也是空串才能匹配
        dp[0][0] = true;

        //初始化第一行
        //此时s是空串,p只要偶数位置为*号,就可以匹配为空
        for(int j = 2; j<=n; j += 2)
        {
            if(p[j] == '*')
                dp[0][j] = true;
            else 
                break;
        }

        //3、填表
        for(int i = 1; i<=m; ++i)
        {
            for(int j = 1; j<=n; ++j)
            {
                if(p[j] == '*')
                {
                    //p[j-1]是‘.’,匹配空串/匹配多个
                   dp[i][j] = dp[i][j-2] || (p[j-1] == '.' || p[j-1] == s[i]) && dp[i-1][j];
                }
                else
                {
                    dp[i][j] = (s[i] == p[j] || p[j] == '.') && dp[i-1][j-1];
                }
            }
        }

        //4、返回值
        return dp[m][n];

    }
};

  题四十五 交错字符串

LCR 096. 交错字符串 - 力扣(LeetCode)

题解:

dp[i][j]:[0,i]的s1,和[0,j]的s2能否匹配s3[0,j+i-1]

s1和s2能够匹配s3:必须满足一个条件,就s3的最后一个元素,要么等于s1的最后一个元素,要么等于s2的最后一个元素
据此分析,分类讨论:
1、s1最后一个位置s1[ i ]匹配s3最后一个位置s3[ j+i-1 ]
2、s2最后一个位置s2[ j ]匹配s3最后一个位置s3[ j+i-1 ]

初始化,多加一行一列。
第一行:s1串为空,s3串只能由s2串匹配,从前往后遍历,一个一个对比,相等就是true。
第一列同理。
自己画图推理。
 

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        //1、创建dp表
        int m = s1.size();
        int n = s2.size();
        if(m+n < s3.size())
            return false;
        
        vector<vector<bool>> dp(m+1, vector<bool> (n+1));
        

        //2、初始化
        s1 = " " + s1;
        s2 = " " + s2;
        s3 = " " + s3;
        dp[0][0] = true;
        //初始化第一行,s1为空
        for(int i = 1; i<=n; ++i)
        {
            if(s2[i] == s3[i])
                dp[0][i] = true;
            else 
                break;
        }

        //初始化第一列,s2为空
        for(int i = 1; i<=m; ++i)
        {
            if(s1[i] == s3[i])
                dp[i][0] = true;
            else 
                break;
        }

        //3、填表
        for(int i = 1; i<=m; ++i)
        {
            for(int j = 1; j<=n; ++j)
            {
                dp[i][j] = (dp[i-1][j] && s1[i] == s3[i+j]) ||
                            (dp[i][j-1] && s2[j] == s3[i+j]);
            }
        }

        //4、返回值
        return dp[m][n];

    }
};

  题四十六 两个字符串的最小ASCII删除和

712. 两个字符串的最小ASCII删除和 - 力扣(LeetCode)

题解:

正难则反,逆向思维:
找所有公共子序列中,ASCII码最大的那个
然后和减去两次该值,即是最小的删除值。
dp[i][j]:s1[0,i]的字串,和s2[0,j]的所有公共子序列中,ACSII码最大的值。
我们现在只研究这两个区间,画图分析。
按照s1和s2的最后一个位置,分情况讨论:
1、s1[i] == s2[j]
2、公共子序列包含s1[i]
3、公共子序列包含s2[j]
4、公共子序列均不包含s1[i],s2[j]

class Solution {
public:
    int minimumDeleteSum(string s1, string s2) {
        //1、创建dp表
        int m = s1.size();
        int n = s2.size();
        vector<vector<int>> dp(m+1, vector<int> (n+1));

        //2、初始化 
        s1 = " " + s1;
        s2 = " " + s2; 

        //3、填表
        for(int i = 1; i<=m; ++i)
        {
            for(int j = 1; j<=n; ++j)
            {
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
                if(s1[i] == s2[j])
                {
                    dp[i][j] = max(dp[i][j], dp[i-1][j-1] + s1[i]);
                }
               
            }
        }

        //4、返回值
        int sum = 0;
        for(int i = 1; i<=m; ++i)
            sum += s1[i];
        for(int j = 1; j<=n; ++j)
            sum += s2[j];
        
        return sum - 2*dp[m][n];
        
        

    }
};

 题四十七 交最长重复子数组

718. 最长重复子数组 - 力扣(LeetCode)

题解:

子数组和子序列的区别:
子数组要求必须连续,不可分割;
而子序列不要求连续。
dp[i][j]:nums1以i为结尾的,和以为j为结尾的nums2两个数组中,公共的 、长度最长的子数组的长度。
根据此状态转移方程推导,画图分析。

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        //1、创建dp表
        int m = nums1.size();
        int n = nums2.size();
        vector<vector<int>> dp(m+1, vector<int> (n+1));

        //2、初始化

        //3、填表
        int ret = 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;
                ret = max(ret, dp[i][j]);
            }
        }

        //4、返回值
        return ret;


    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二十5画生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值