当前位置: 首页 > news >正文

【动态规划篇】最少分割回文 编辑距离 不同的子序列

🌠作者:@阿亮joy.
🎆专栏:《数据结构与算法要啸着学》
🎇座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
在这里插入图片描述


目录

    • 👉最少分割回文👈
    • 👉编辑距离👈
    • 👉不同的子序列👈
    • 👉总结👈

👉最少分割回文👈

给定一个字符串 s,请将 s 分割成一些子串,使每个子串都是回文串。返回符合要求的最少分割次数 。

在这里插入图片描述

思路:

在这里插入图片描述

class Solution 
{
private:
    bool isPalindrome(const string& s, int begin, int end)
    {
        while(begin < end)
        {
            if(s[begin] != s[end])
            {
                return false;
            }
            ++begin;
            --end;
        }
        return true;
    }

public:
    int minCut(string s) 
    {
        int len = s.size();
        vector<int> minCut(len + 1);
        // 初始状态:F(i) = i - 1
        for(int i = 1; i <= len; ++i)
        {
            minCut[i] = i - 1;
        }

        for(int i = 2; i <= len; ++i)
        {
            // [1, i]整体是回文串
            if(isPalindrome(s, 0, i - 1))
            {
                minCut[i] = 0;
                continue;
            }
            // j < i && [j + 1, i]是否为回文串
            for(int j = 1; j < i; ++j)
            {
                if(isPalindrome(s, j, i - 1))
                {
                    minCut[i] = min(minCut[i], minCut[j] + 1);
                }
            }
        }
        return minCut[len];
    }
};

在这里插入图片描述

class Solution 
{
private:
    bool isPalindrome(const string& s, int begin, int end)
    {
        while(begin < end)
        {
            if(s[begin] != s[end])
            {
                return false;
            }
            ++begin;
            --end;
        }
        return true;
    }

public:
    int minCut(string s) 
    {
        int len = s.size();
        vector<int> minCut(len + 1);
        // 初始状态:F(i) = i - 1
        for(int i = 0; i <= len; ++i)
        {
            minCut[i] = i - 1;
        }
        // F(0) = -1,若整个字符串为回文串,也能得出最小分割次数为0
        for(int i = 2; i <= len; ++i)
        {
            // j < i && [j + 1, i]是否为回文串
            for(int j = 0; j < i; ++j)
            {
                if(isPalindrome(s, j, i - 1))
                {
                    minCut[i] = min(minCut[i], minCut[j] + 1);
                }
            }
        }
        return minCut[len];
    }
};

在这里插入图片描述
上面的解法是 O(N ^ 3) 的算法,两层 for 循环再加一个判断回文串 O(N) 的算法,所以这个解法的时间复杂度为 O(N ^ 3)。我们可以先将回文串的结果保存下来,要用时可以直接用,这样就可以将时间复杂度将到 O(N ^ 2) 了。

判断回文串也是需要用到动态规划的。

在这里插入图片描述

class Solution 
{
private:
    vector<vector<bool>> getMat(const string& s)
    {
        int n = s.size();
        vector<vector<bool>> Mat(n, vector<bool>(n, false));

        // 从矩阵的右下角开始更新
        for(int i = n - 1; i >= 0; --i)
        {
            for(int j = i; j < n; ++j)
            {
                if(j == i)  // 初始状态
                    Mat[i][j] = true;
                else if(j == i + 1)
                    Mat[i][j] = s[i] == s[j];
                else    
                    Mat[i][j] = ((s[i] == s[j]) && (Mat[i + 1][j - 1]));
            }
        }

        return Mat;
    }

public:
    int minCut(string s) 
    {
        int len = s.size();
        vector<vector<bool>> Mat = getMat(s);
        vector<int> minCut(len + 1);
        // 初始状态:F(i) = i - 1
        for(int i = 0; i <= len; ++i)
        {
            minCut[i] = i - 1;
        }
        // F(0) = -1,若整个字符串为回文串,也能得出最小分割次数为0
        for(int i = 2; i <= len; ++i)
        {
            // j < i && [j + 1, i]是否为回文串
            for(int j = 0; j < i; ++j)
            {
                if(Mat[j][i - 1])
                {
                    minCut[i] = min(minCut[i], minCut[j] + 1);
                }
            }
        }
        return minCut[len];
    }
};

在这里插入图片描述
该解法的时间复杂度为 O(N ^ 2),空间复杂度为 O(N ^ 2),典型的以空间换时间的做法。

👉编辑距离👈

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

在这里插入图片描述

思路:

在这里插入图片描述

class Solution 
{
public:
    int minDistance(string word1, string word2) 
    {
        int row = word1.size();
        int col = word2.size();

        vector<vector<int>> minEdit(row + 1, vector<int>(col + 1));
        // 初始状态:F(i, 0) = 0 和 F(0, j) = j
        for(int i = 0; i <= row; ++i)   minEdit[i][0] = i;
        for(int j = 1; j <= col; ++j)   minEdit[0][j] = j;

        for(int i = 1; i <= row; ++i)
        {
            for(int j = 1; j <= col; ++j)
            {
                // 插入和删除
                minEdit[i][j] = min(minEdit[i][j - 1], minEdit[i - 1][j]) + 1;
                // 替换 word1[i - 1]是否等于word2[j - 1]
                if(word1[i - 1] == word2[j - 1])
                {
                    minEdit[i][j] = min(minEdit[i][j], minEdit[i - 1][j - 1]);
                }
                else
                {
                    minEdit[i][j] = min(minEdit[i][j], minEdit[i - 1][j - 1] + 1);
                }
            }
        }
        return minEdit[row][col];
    }
};

在这里插入图片描述

👉不同的子序列👈

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。

字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)

题目数据保证答案符合 32 位带符号整数范围。

在这里插入图片描述

思路:

在这里插入图片描述

class Solution 
{
public:
    int numDistinct(string s, string t) 
    {
        int row = s.size();
        int col = t.size();
        
        vector<vector<unsigned int>> dp(row + 1, vector<unsigned int>(col + 1, 0));
        for(int i = 0; i <= row; ++i)   dp[i][0] = 1;   // 初始状态
        for(int i = 1; i <= row; ++i)
        {
            for(int j = 1; j <= col; ++j)
            {
                if(s[i - 1] == t[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
                else
                    dp[i][j] = dp[i - 1][j];
            }
        }
        return dp[row][col];
    }
};

在这里插入图片描述

空间复杂度优化

由于当前的dp[i][j]仅取决于dp[i - 1][j - 1]dp[i - 1][j],所以我们不需要用二维数组将所有状态的解都保留下来,可以用一维数组将上一次的解保留下来就行了。但需要注意的是,需要从后向前更新,不然无法拿到上一次的解。

class Solution 
{
public:
    int numDistinct(string s, string t) 
    {
        int row = s.size();
        int col = t.size();
        
        // 初始状态
        vector<unsigned int> dp(col + 1, 0);
        dp[0] = 1;
        for(int i = 1; i <= row; ++i)
        {
            for(int j = col; j >= 1; --j)
            {
                if(s[i - 1] == t[j - 1])
                    dp[j] = dp[j - 1] + dp[j];  // dp += dp[i - 1]
                
                // s[i - 1] != t[j - 1]时,不需要更新dp[j]
            }
        }
        return dp[col];
    }
};

在这里插入图片描述

👉总结👈

动态规划的难点:

  • 状态如何定义:从问题中抽象出状态,每一个状态都对应着一个子问题。状态的定义可能不止一种方式,那如何定义状态的合理性呢?某一个状态的阶或者多个状态的解能否推出最终问题的解,也就是状态之间能够形成递推关系(状态转移方程)。
  • 一维状态 VS 二维状态:首先尝试一维状态,如果一维状态的合理性不满足时,再去尝试二维状态。
  • 常见问题的状态:字符串:状态一般对应子串,状态一般每次增加一个新的字符。二维状态有时候能够优化成一维状态。

那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️

相关文章:

  • 嵌入式走过的路
  • 【面试高频题】难度 2/5,回溯算法经典运用
  • 程序员必备网站,建议收藏!
  • (四)汇编语言——简单程序
  • 【OpenFeign】【源码+图解】【六】创建FeignClient接口的代理(下)
  • Minecraft(我的世界) Fabric 1.19.3 服务器搭建教程
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • 汽车OTA概述
  • 基于Java+Swing+mysql餐厅点餐管理系统
  • 店铺如何快速实现数字化管理?不妨参考一下管理系统
  • 修改后的代码只进行了git add操作不小心给他恢复了怎么找回来
  • JUC(一):线程池
  • org.springframework.jdbc.BadSqlGrammarException: Error updating database
  • 熟人服务器被黑,五种实战方法强化linux服务器安全性!
  • RabbitMQ总结
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • Docker 笔记(2):Dockerfile
  • es6(二):字符串的扩展
  • github从入门到放弃(1)
  • Mac转Windows的拯救指南
  • Protobuf3语言指南
  • Redux 中间件分析
  • SQLServer之创建数据库快照
  • Webpack 4 学习01(基础配置)
  • Yii源码解读-服务定位器(Service Locator)
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 坑!为什么View.startAnimation不起作用?
  • 详解NodeJs流之一
  • 用mpvue开发微信小程序
  • 走向全栈之MongoDB的使用
  • python最赚钱的4个方向,你最心动的是哪个?
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • 第二十章:异步和文件I/O.(二十三)
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #FPGA(基础知识)
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (C语言)字符分类函数
  • (Java)【深基9.例1】选举学生会
  • (编译到47%失败)to be deleted
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (一)基于IDEA的JAVA基础1
  • (转)编辑寄语:因为爱心,所以美丽
  • (转)创业的注意事项
  • (轉)JSON.stringify 语法实例讲解
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET MVC之AOP
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .NET面试题解析(11)-SQL语言基础及数据库基本原理
  • .pyc文件是什么?
  • :中兴通讯为何成功