【动态规划】两个数组问题

简介: 【动态规划】两个数组问题

动态规划(两个数组问题)

1. 最长公共子序列

题目链接

  1. 状态表示
    dp[i][j] 表示 s1 0 到 i 区间内,以及时s2 0 到 j 区间内所有的子序列中,最长的公共子序列
  2. 状态转移方程
    根据最后一个位置的请款分类讨论。

  3. 初始化
    关于字符串的dp问题,空串是有研究意义的。引入空串的概念之后会方便我们的初始化
    这里还可以使用之前的方法,使用辅助节点的方式
  1. 填表顺序
    从下往上,从左到右
  2. 返回值

AC代码:

class Solution 
{
public:
    int longestCommonSubsequence(string text1, string text2) 
    {
        int n = text1.size();
        int m = text2.size();
        vector<vector<int>> dp(n + 1, vector<int>(m + 1));
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                if (text1[i - 1] == text2[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[n][m];
    }
};

2. 不相交的线

题目链接

题目分析:根据这个题目可以看到不就是找最长公共子序列嘛

  1. 状态表示
    dp[i][j] 表示 0 到 i 的所有子序列以及 0 到 j 的所有子序列当中,最长的公共子序列
  2. 状态转移方程

  1. 初始化
  2. 填表
  3. 返回值

AC代码:

class Solution 
{
public:
    int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) 
    {
        int n = nums1.size();
        int m = nums2.size();
        vector<vector<int>> dp(n + 1, vector<int>(m + 1));
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; 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]);
            }
        }
        return dp[n][m];
    }
};

3. 不同的子序列

题目链接

  1. 状态表示
    dp[i][j] 表示 0 到 j 之间的所有子序列中有多少个出现 目标中0 到 i 之间的字母
  2. 状态转移方程

  1. 初始化
  2. 填表
  3. 返回值

AC代码:

class Solution 
{
public:
    int numDistinct(string s, string t) 
    {
        int n = s.size();
        int m = t.size();
        vector<vector<double>> dp(m + 1, vector<double>(n + 1));
        for (int i = 0; i <= n; i++) dp[0][i] = 1;
        for (int i = 1; i <= m; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                dp[i][j] += dp[i][j - 1];
                if (s[j - 1] == t[i - 1]) dp[i][j] += dp[i - 1][j - 1];
            }
        }
        return dp[m][n];
    }
};

4. 交错字符串

题目链接

  1. 状态表示
    dp[i][j] 表示 s1中[1, i]之间的字符串,以及s2当中[1,j]之间的字符串能否拼接成s3当中[1, i + j]之间的字符串
  2. 状态转移方程

  3. 初始化
    当两个字符串都是空的时候,返回的一定是true,当s1 不为空, s2 为空,只要s1有一个不等于s3则直接返回false否则为true
    s2的初始化也一样
  1. 填表
    从上到下,从左到右
  2. 返回值

AC代码:

class Solution 
{
public:
    bool isInterleave(string s1, string s2, string s3) 
    {
        int m = s1.size();
        int n = s2.size();
        if (m + n != s3.size()) return false;
        s1 = " " + s1;
        s2 = " " + s2;
        s3 = " " + s3;
        vector<vector<bool>> dp(m + 1, vector<bool>(n + 1));
        dp[0][0] = true;
        for (int i = 1; i <= n; i++) 
        {
            if (s2[i] == s3[i]) dp[0][i] = true;
            else break;
        }
        for (int i = 1; i <= m; i++) 
        {
            if (s1[i] == s3[i]) dp[i][0] = true;
            else break;
        }
        for (int i = 1; i <= m; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if ((s1[i] == s3[i + j] && dp[i - 1][j]) || (s2[j] == s3[i + j] && dp[i][j - 1]))
                    dp[i][j] = true;
            }
        }
        return dp[m][n];
    }
};

5. 两个字符串的最小ASCII和

题目链接

  1. 状态表示
    分析,这个题目要找两个字符串最小的,可以找到公共最大的然后反向求
    dp[i][j] 表示 [0, i] 以及[0, j]的所有子序列当中,ASCII最大的公共子序列的和
  2. 状态转移方程

  3. 初始化
    无需初始化
  4. 填表
    从左到右,从上到下
  5. 返回值
    两个字符串的和前去最大的乘以2

AC代码:

class Solution 
{
public:
    int minimumDeleteSum(string s1, string s2) 
    {
        int m = s1.size();
        int n = s2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        for (int i = 1; i <= m; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
                if (s1[i - 1] == s2[j - 1])
                {
                    dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + s1[i - 1]);
                }
            }
        }
        int sum = 0;
        for (auto e : s1) sum += e;
        for (auto e : s2) sum += e;
        return sum - dp[m][n] - dp[m][n];
    }
};

6. 最长重复子数组

题目链接

  1. 状态表示
    dp[i][j]以 i 位置为结尾的所有子数组,以及以j 位置为结尾的所有子数组当中,最长公共子数组的长度
  2. 状态转移方程

  3. 初始化
    无需初初始化
  1. 填表
  2. 返回值

AC代码:

class Solution 
{
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) 
    {
        int m = nums1.size();
        int n = nums2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        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]);
            }
        }
        return ret;
    }
};

7. 通配符匹配

题目链接

  1. 状态表示
    dp[i][j] 表示 p[0, j]区间内子串能否匹配s[0, i]区间内的子串
  2. 状态转移方程

    这种有很多表示方法的状态转移方程,需要优化不可能把每个状态都表示出来
    第一种就是采用数学方法来优化:

还可以根据状态表示以及实际情

  1. 况优化状态转移方程

  2. 初始化
    p[0][j] == ”*“ 时就一直是true
  1. 填表
  2. 返回值

AC代码:

class Solution 
{
public:
    bool isMatch(string s, string p) 
    {
        int m = s.size();
        int n = p.size();
        s = " " + s, p = " " + p;
        vector<vector<bool>> dp(m + 1, vector<bool>(n + 1));
        dp[0][0] = true;
        for (int j = 1; j <= n; j++)
        {
            if (p[j] == '*') dp[0][j] = 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] || dp[i][j - 1];
                else if (p[j] == '?') dp[i][j] = dp[i - 1][j - 1];
                else 
                {
                    if (s[i] == p[j]) dp[i][j] = dp[i - 1][j - 1];
                }
            }
        }
        return dp[m][n];
    }
};
相关文章
|
Linux iOS开发 MacOS
brew - mac 下的 brew 切换为国内源
brew - mac 下的 brew 切换为国内源
4970 0
|
物联网 Linux 流计算
EasyLogger--不一样的打印输出
EasyLogger--不一样的打印输出
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
395 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
|
缓存 数据库 NoSQL
【后端面经】【缓存】33|缓存模式:缓存模式能不能解决缓存一致性问题?-02 Write Through + Write Back
【5月更文挑战第10天】`Write Through`是一种缓存策略,写操作仅需写入缓存,缓存负责更新数据库。异步版本可能丢失数据,而同步变种先写数据库再异步刷新缓存,减少丢数据风险。`Write Back`模式数据先写入缓存,过期时才写入数据库,可能导致数据丢失,但若使用Redis并确保高可用,可部分解决一致性问题。在特定条件下,如使用SETNX命令,能缓解一致性挑战。
335 0
【后端面经】【缓存】33|缓存模式:缓存模式能不能解决缓存一致性问题?-02 Write Through + Write Back
|
安全 Python
Elasticsearch 删除重复文档实现方式,你知道几个?
Elasticsearch 删除重复文档实现方式,你知道几个?
|
数据安全/隐私保护
重装系统攻略
重装系统攻略
|
数据安全/隐私保护 Unix 芯片
wpa_cli和wpa_supplicant使用,配置无线AP名和密码,静态ip地址
配置静态ip方法分享:通过串口命令行输入如下命令:     1. 添加无线网络接入点(AP) 及其 密码:# wpa_cli -p /data/misc/wpa_supplicantwpa_cli v0.
2516 0
|
测试技术
Burp suite无法拦截127.0.0.0数据包的解决方案
Burp suite无法拦截127.0.0.0数据包的解决方案
1505 0
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch中级教程:深入理解自动求导和优化
在你已经掌握了如何使用PyTorch构建神经网络的基础上,接下来我们将深入探讨PyTorch的两个核心特性:自动求导(Autograd)和优化(Optimization)。这两个特性在深度学习模型的训练过程中起着至关重要的作用。
|
存储 Kubernetes 容灾
技术揭秘:从双11看实时数仓Hologres高可用设计与实践
本文将会从阿里巴巴双11场景出发,分析实时数仓面临的高可用挑战以及针对性设计。
4941 3
技术揭秘:从双11看实时数仓Hologres高可用设计与实践