引言

下面的四种题相互间都有联系,都是类似编辑距离类的题目,这里从简单开始,逐渐深入;

判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

示例 1:
输入:s = “abc”, t = “ahbgdc”
输出:true

示例 2:
输入:s = “axc”, t = “ahbgdc”
输出:false

提示:

0 <= s.length <= 100
0 <= t.length <= 10^4
两个字符串都只由小写字符组成。

这道题我们其实只需要计算删除的情况就可以;(其实可以想象为把 t 的每一个和 s 不相等的字符删除直到和 s 一样)
1,dp[i][j]表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度;

2,这里就有两种情况:
s[i - 1] == t[j - 1] 此时就说明 s 中的一个字符在 t 中存在;即dp[i][j] = dp[i - 1][j - 1] + 1;
s[i - 1] != t[j - 1] 这时就需要删除 t 的这个字符,继续和 s 匹配;即dp[i][j] = dp[i][j - 1]

3,初始化时发现dp[i][0]和dp[0][j]是没有意义的,但是为了转移方程的计算,还是需要初始化为0;

4,由转移方程可知道遍历顺序是从前到后,从上到下;
代码如下:

class Solution {public:bool isSubsequence(string s, string t) {int len1 = s.size(), len2 = t.size();vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));for (int i = 1; i <= len1; ++i) {for (int j = 1; j <= len2; ++j) {if (s[i - 1] == t[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = dp[i][j - 1]; //j相当于一个删除的操作;}}return dp[len1][len2] == len1;}
};

这道题只涉及了一个类似删除操作,因为t中字符与s一个不同就删一个,直到留下最后相同的;下面再看一道:

不同的子序列

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
题目数据保证答案符合 32 位带符号整数范围。


提示:

0 <= s.length, t.length <= 1000
s 和 t 由英文字母组成

这道题相对上一道就稍微复杂一些,但是依然可以看出只涉及了删除操作;
1,dp[i][j]表示以 i-1 为结尾的 s 子序列中出现的以 j-1 为结尾的 t 的个数为dp[i][j];

2,同样分为两种情况:

当s[i - 1] 与 t[j - 1]相等时,有两部分
一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1];
另一部分是不用s[i - 1]匹配,个数为dp[i - 1][j];
为什么不用s[i - 1]匹配呢?比如s为raa,t为ra,我可以用s[i - 1]匹配,则是s[0],s[2],我如果不用s[i - 1]匹配,则是s[0],s[1];
所以这种情况都需要概况上;
即dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]:

当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配,即:dp[i - 1][j],
其实这里 i - 1 就可以理解为一个删除操作,将因为不匹配,所以减小匹配范围

3,初始化这里需要注意,
dp[i][0]表示以i-1为结尾的s可以随便删除元素,出现空字符串的个数。
则dp[i][0]一定都是1,因为也就是把以i-1为结尾的s,删除所有元素,出现空字符串的个数就是1。

dp[0][j]则是s为空,那么怎么都无法匹配到t,所以都为0;

dp[0][0] 则代表空字符串s,删除0个元素变成t,所以为1;

4,遍历顺序很容易就看出来从前到后,从上到下;

代码如下:

class Solution {public:int numDistinct(string s, string t) {int len1 = s.size(), len2 = t.size();vector<vector<unsigned long long>> dp(len1 + 1, vector<unsigned long long>(len2 + 1));//防止超限for (int i = 0; i <= len1; ++i) dp[i][0] = 1;//i为0是因为要把dp[0][0]初始化为0for (int i = 1; i <= len2; ++i) dp[0][i] = 0;for (int i = 1; i <= len1; ++i) {for (int j = 1; j <= len2; ++j) {if (s[i - 1] == t[j - 1])dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];elsedp[i][j] = dp[i - 1][j];}}return dp[len1][len2];}
};

这道题也就有点难度了,但是总归还是只有一个删除操作,看下面这道题

两个字符串的删除操作

给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。

示例:
输入: “sea”, “eat”
输出: 2
解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"

提示:
给定单词的长度不超过500。
给定单词中的字符只含有小写字母。

这道题目就需要删除两个字符串了,但是方法还是一样的;
1,dp[i][j]表示以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数

2,这里的转移方程依旧分为两种情况:
当word1[i - 1] 与 word2[j - 1]相同的时候,dp[i][j] = dp[i - 1][j - 1];

当word1[i - 1] 与 word2[j - 1]不相同的时候,有三种情况:
一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1
二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1
三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2
最后取三者最小值即可,dp[i][j] = min(min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 2);

3,初始化需要注意
当word2为空串,那么dp[i][0]就是以i-1为结尾的字符串word1要删除多i个元素,才能和word2相同,即dp[i][0] = i。
当word1为空串,那么dp[0][j]就是以j-1为结尾的字符串word2要删除j个元素,才能和word1相同,即dp[i][0] = i。

4,遍历顺序很容易看出来,从上到下,从左到右;

代码如下:

class Solution {public:int minDistance(string word1, string word2) {int len1 = word1.size(), len2 = word2.size();vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1));for (int i = 0; i <= len1; ++i) dp[i][0] = i;for (int i = 0; i <= len2; ++i) dp[0][i] = i;for (int i = 1; i <= len1; ++i) {for (int j = 1; j <= len2; ++j) {if (word1[i - 1] == word2[j - 1])dp[i][j] = dp[i - 1][j - 1];elsedp[i][j] = min(min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 2);}}return dp[len1][len2];}
};

最后的大boss终于要来了,但是通过前三道题,应该对这种题目有点感觉了吧;

编辑距离

给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数
你可以对一个单词进行如下三种操作
插入一个字符
删除一个字符
替换一个字符

示例 1:
输入:word1 = “horse”, word2 = “ros”
输出:3
解释:
horse -> rorse (将 ‘h’ 替换为 ‘r’)
rorse -> rose (删除 ‘r’)
rose -> ros (删除 ‘e’)

示例 2:
输入:word1 = “intention”, word2 = “execution”
输出:5
解释:
intention -> inention (删除 ‘t’)
inention -> enention (将 ‘i’ 替换为 ‘e’)
enention -> exention (将 ‘n’ 替换为 ‘x’)
exention -> exection (将 ‘n’ 替换为 ‘c’)
exection -> execution (插入 ‘u’)

提示:

0 <= word1.length, word2.length <= 500
word1 和 word2 由小写英文字母组成

这道题就有了三步操作,但是不管怎么样,和之前的几道题都是一模一样的;
1,dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j];

2,这里还是两种情况
如果word1[i - 1] == word2[j - 1]时,不需要任何操作,直接让dp[i][j] = dp[i - 1][j - 1];

如果word1[i - 1] != word2[j - 1],这时候分为三部,对应的就是三种操作
一:word1增加一个元素,使word1[i - 1]与word2[j - 1]相同,即 dp[i][j] = dp[i - 1][j] + 1
二:word1减去一个元素,即word2增添一个元素,使word1[i - 1]与word2[j - 1]相同,即 dp[i][j] = dp[i][j - 1] + 1;
三:替换操作,word1替换word1[i - 1],使其与word2[j - 1]相同,即 dp[i][j] = dp[i - 1][j - 1] + 1;

3,初始化dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,则只需要对word1做减去操作即可
dp[i][0] = i;
dp[0][j] = j同理;

4,循环顺序老套路;
代码如下:

class Solution {public:int minDistance(string word1, string word2) {int len1 = word1.length();int len2 = word2.length();vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1));for (int j = 1; j <= len2; ++j) dp[0][j] = j;for (int i = 1; i <= len1; ++i) dp[i][0] = i;for (int i = 1; i <= len1; ++i) {for (int j = 1; j <= len2; ++j) {if (word1.at(i - 1) == word2.at(j - 1))dp[i][j] = dp[i - 1][j - 1];elsedp[i][j] = min(min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;}}return dp[len1][len2];}
};

总结

这几道题还是多看看,理解一下,看不下去歇会看,看久了容易晕;

判断子序列不同的子序列两个字符串的删除操作编辑距离相关推荐

  1. Leetcode 583.两个字符串的删除操作

    两个字符串的删除操作 给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符. 示例 1: 输入: "se ...

  2. 583. 两个字符串的删除操作

    583. 两个字符串的删除操作 给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符. 示例: 输入: " ...

  3. 代码随想录算法训练营第五十六天-动态规划16|● 583. 两个字符串的删除操作 ● 72. 编辑距离 ● 编辑距离总结篇

    一.583. 两个字符串的删除操作 给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符. 示例: 输入: &qu ...

  4. 代码随想录算法训练营Day56动态规划:583.两个字符串的删除操作,72.编辑距离

    583.两个字符串的删除操作 文章链接:代码随想录 (programmercarl.com) 思路:动规五步曲 (1)确定dp数组及其含义 dp[i][j]表示字符串1在区间[0, i - 1]和字符 ...

  5. 两个字符串的删除操作

    思路 本题和动态规划:115.不同的子序列相比,其实就是两个字符串都可以删除了,情况虽说复杂一些,但整体思路是不变的. 1. 确定dp数组(dp table)以及下标的含义 dp[i][j]:以i-1 ...

  6. LeetCode 583. 两个字符串的删除操作(动态规划)

    1. 题目 给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符. 示例: 输入: "sea" ...

  7. 583. 两个字符串的删除操作(JavaScript)

    //找不连续的最长公共子序列 var minDistance = function(w1, w2) {let len1=w1.lengthlet len2=w2.lengthlet dp=new Ar ...

  8. LeetCode 583 两个字符串的删除操作

    思路: 最小操作步数=word1.size+word2.size-2*最长公共子序列长度 (删除word1,word2中的字符只保留它们两个最长公共子序列) 问题转化为求两个字符串的最长公共子序列 动 ...

  9. 两个字符串的删除操作Python解法

    给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符. 例: 输入: "sea", " ...

最新文章

  1. flash build 4.6 不能debug 报错 C:\WINDOWS\system32\...
  2. Halcon初学者知识:用set_paint直观显示图像的属性
  3. python中mat函数_python matplotlib中的subplot函数使用详解
  4. 微信作为一种提供即时通讯服务器,Golang 写的即时通讯服务器 im(服务组件形式)...
  5. #include *.c文件的妙用
  6. Java byte类型转换成int类型时需要 0XFF的原因
  7. dhl:指定的命名连接在配置中找不到、非计划用于 EntityClient 提供程序或者无效。...
  8. windows下配置apache和php,Windows系统下Apache和PHP的安装和基本配置
  9. 第 1 章 FreeBSD Install
  10. 营口(熊岳)温泉旅游归来!
  11. InnoDB存储引擎MVCC实现原理
  12. 自然语言处理与企业对话系统设计
  13. 在不知道密码情况下卸载企业版360
  14. 完美解决Mac无法写入NTFS硬盘——Mounty for NTFS
  15. [精简]托福核心词汇103
  16. matlab迭代求解泊松方程,MATLAB编程求解二维泊松方程
  17. java判断白天黑夜,获取Java中的所有夜晚时间
  18. Android 获取最近几天的日历日程
  19. 盘点IT行业“中国式合伙人”的离合春秋
  20. 软考有哪些实质性的用处?

热门文章

  1. mysql 隐式锁和显示锁_锁的类型以及粒度,两段锁协议,隐式和显示锁
  2. linux内核红宝书,Solaris 10红宝书 9.3
  3. 数据中心更新那么快,运维人如何度过35岁危机?
  4. java jpopupmenu 无法显示_java – 从JButton显示/隐藏JPopupMenu; FocusListener无法正常工作?...
  5. python读取data_转载 “ 理想国@Data ”重拾Python(5):数据读取 博客
  6. 成功解决TypeError: read_excel() got an unexpected keyword argument ‘parse_cols or ‘sheetname‘
  7. 成功解决利用matplotlib.pyplot进行绘图的时候整个画布中的绘制曲线只显示一部分
  8. Interview:算法岗位面试—10.29下午上海某电子(偏传统ML算法,外企)数据结构算法+晚上国内某保险公司(偏AI算法,世界500强)技术面试之分类算法、回归算法、聚类算法等细节考察
  9. ML之PLiR之LARS:利用LARS算法求解ElasticNet回归类型问题(实数值评分预测)
  10. TF之CNN:基于CIFAR-10数据集训练、检测CNN(2+2)模型(TensorBoard可视化)