两个数组的dp问题(C++)

文章目录

  • 前言
  • 一、1143. 最长公共子序列
    • 1.状态表示
    • 2.状态转移方程
    • 3.初始化
    • 4.填表顺序
    • 5.返回值是什么
    • 6.代码编写
  • 二、44. 通配符匹配
    • 1.状态表示
    • 2.状态转移方程
    • 3.初始化
    • 4.填表顺序
    • 5.返回值是什么
    • 6.代码编写
  • 三、712. 两个字符串的最小ASCII删除和
  • 总结


前言

一、1143. 最长公共子序列

1143. 最长公共子序列

看完解析之后可以尝试分析一下这道题目
1035. 不相交的线

1.状态表示

解决此类题目我们的经验就是: 以第一个字符串的区间【0,i】以及第二个字符串的区间【0,j】为研究对象,根据题目要求确立状态表示

根据这个题目,状态标识可以设置为
第一个字符串的区间【0,i】以及第二个字符串的区间【0,j】为结尾的所有子序列中最长公共子序列的长度

2.状态转移方程

我们依然是根据经验:根据最后一个位置,分情况讨论

对于dp【i】【j】,我们可以对s【i】和s【j】的情况进行讨论。
s【i】和s【j】相等,那就说明最长公共子序列一定是以i和j为结尾的。
我们只需要找到【0,i-1】和【0,j-1】区间内最长子序列长度再加上1就是我们要找的dp值。
s【i】和s【j】不相同,那就说明在这个区间内最仓公共子序列不是以i和j同时结尾的,我们只需要在这里面找最大值就可以,dp【i-1】【j】,dp【i】【j-1】,dp【i-1】【j-1】。

这里还有一个问题,【i-1】【j-1】区间包括在另外两个区间里面了,我们要不要将他舍去呢???
根据本道题木,我们要求的是最长的长度,舍去对结果没有影响们可以舍去。

3.初始化

关于此类字符串dp问题,我们要考虑空串的情况。
考虑空串之后,可以很方便我们进行初始化,多加上一行和一列就可以。
同时要保证后序填表正确和下标的映射关系。
对于初始化的值,我们把这一行和一列初始化为0就可以。
对于下标的映射关系,字符串可以采用在前面多加一个字符来解决。

4.填表顺序

从左到右,从上到下

5.返回值是什么

返回dp[ m ][ n]

6.代码编写

class Solution {
public:
    int longestCommonSubsequence(string s1, string s2) 
    {
        int m=s1.size();
        int n=s2.size();
        //1.建表+初始化
        vector<vector<int>>dp(m+1,vector<int>(n+1,0));
        //2.处理字符串,方便填表
        s1=" "+s1;
        s2=" "+s2;
        //3.填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(s1[i]==s2[j]) 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];

    }
};

二、44. 通配符匹配

44. 通配符匹配

1.状态表示

我们根据经验+题目要求,确定出状态标示:
p【0,j】区间内的字串能否匹配s【0,i】区间的字串。

2.状态转移方程

根据最后一个位置的情况划分问题

我们可以知道p【j】有三种情况,分析一下。
p【j】是普通小写字母。如果能够匹配,首先要保证最后一个位置的字母相同即可。随后在p【0,j-1】区间内判断s【0,i-1】区间内能否匹配就可以,这正好就是我们的状态表示,dp【i-1】【j-1】。
p【j】是‘ ?’,?可以匹配任意一个字母,那么最后一个位置肯定可以匹配,我么只需要判断剩余区间就可以,也就是判断dp【i-1】【j-1】.
p【j】是‘ * ’,它可以匹配多个字母,但是在题目中我们并不能确定这个星号需要匹配多少个字母,我们逐个分析一下。

 假设星号一个也不匹配,就是匹配空串,我们就只需要判断dp【i】【j-1】
 假设星号匹配1个,我们就只需要判断dp【i-1】【j-1】
 假设星号匹配2个,我们就只需要判断dp【i-2】【j-1】
 假设星号匹配3个,我们就只需要判断dp【i-3】【j-1】
 … … … … …
 假设星号匹配i个,我们就只需要判断dp【0】【j-1】

这些情况我们需要挨个判断,只要有一个结果为true,dp【i】【j】就为true.

我们在这里可以进行一些小优化:
当 dp[j] == ‘*’ 时,状态转移方程为:
dp[i][j] = dp[i][j - 1] || dp[i - 1][j - 1] || dp[i - 2][j - 1] …
我们发现 i 是有规律的减小的,因此我们去看看 dp[i - 1][j]
dp[i - 1][j] = dp[i - 1][j - 1] || dp[i - 2][j - 1] || dp[i - 3][j - 1] …
我们惊奇的发现, dp[i][j] 的状态转移方程里面除了第⼀项以外,其余的都可以用 dp[i - 1][j] 替代。
因此,我们优化我们的状态转移方程为: dp[i][j] = dp[i - 1][j] || dp[i][j - 1] 。

3.初始化

我们在处理字符串问题时候,要考虑空串。
空串:多开一行和一列解决,同时保证辅助结点里面的值要保证后续填表是正确的,还要注意下标的映射关系。

根据分析,dp【0】【0】为true;
这里不要把其余位置都设置为false.
*注意:可以匹配空串,所以第一行需要特殊判断。

4.填表顺序

填表的顺序是「从上往下,从左往右」

5.返回值是什么

返回dp【m】【n】的值即可

6.代码编写

class Solution {
public:
    bool isMatch(string s, string p) 
    {
        int m=s.size();
        int n=p.size();
        //建表
        vector<vector<bool>>dp(m+1,vector<bool>(n+1,false));
        //初始化

        dp[0][0]=true;
        s=" "+s;
        p=" "+p;

        //*很特殊,可以匹配空串
        for(int i=1;i<=n;i++)
        {
            if(p[i]=='*') dp[0][i]=true;
            else break;
        }

        //填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(p[j]=='?') dp[i][j]=dp[i-1][j-1];
                else if(p[j]=='*') dp[i][j]=dp[i][j-1]||dp[i-1][j];
                else 
                {
                    if(s[i]==p[j]&&dp[i-1][j-1]==true)  dp[i][j]=true;
                }
                
            }
        }

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

三、712. 两个字符串的最小ASCII删除和

712. 两个字符串的最小ASCII删除和
本道题木,我们只提供部分讲解,这就是这道题目的精髓

正难则反:我们要求删除的字符最小,那么反过来我们也可以通过找到两个字符串中相同的字符的ASCII码值最大就可以,最后再用总大小减去这个就是我们所要求的结果。

class Solution {
public:
    int minimumDeleteSum(string s1, string s2) 
    {
        int m=s1.size();
        int n=s2.size();
        int sum=0;
        for(int i=0;i<m;i++)
        {
            sum+=s1[i];
        }
        for(int j=0;j<n;j++)
        {
            sum+=s2[j];
        }
        //建表
        vector<vector<int>>dp(m+1,vector<int>(n+1,0));
        //初始化
        s1=" "+s1;
        s2=" "+s2;
        //填表
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(s1[i]==s2[j]) dp[i][j]=dp[i-1][j-1]+s1[i];
                dp[i][j]=max(dp[i][j],max(dp[i-1][j],dp[i][j-1]));
            }
        }  
        return sum-dp[m][n]*2;
    }
};

总结

以上就是今天要讲的内容,本文仅仅详细介绍了 。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~

你可能感兴趣的:(刷题,c++,开发语言)