目录
今天我们继续学习动态规划方面的算法题目,是关于求编辑距离的一系列算法题,我们一起来做一个总结,我先把这些题目复制到上边。
第一题:判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例 1: 输入:s = "abc", t = "ahbgdc" 输出:true
示例 2: 输入:s = "axc", t = "ahbgdc" 输出:false
分析:这道题我们要分两种情况去讨论 :
if(s[i-1]==t[j-1])
t中找到了一个字符在s中也出现了 ;
if(s[i-1]!=t[j-1])
相当于t要删除元素,继续匹配,所以方程就可以列出
if(s[i-1]==t[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=dp[i][j-1];//哪个数组中元素多就删除哪个数组中的元素
因此,coding:
#include <iostream>
using namespace std;
bool isSubquence(string &s,string &t)
{
vector<vector<int>>dp(s.size()+1,vector<int>(t.size()+1));
for(int i=1;i<=s.size();++i)
{
for(int j=1;j<=t.size();++j)
{
if(s[i-1]==t[j-1])
{
dp[i][j]=dp[i-1][j-1]+1;
}
else
{
dp[i][j]=dp[i][j-1]
}
}
}
if(dp[s.size()][t.size()]==s.size())
return true;
return false;
}
int main()
{
string s="ace";
string t="abcse";
bool val=isSubquence(s,t);
cout<<"val="<<val<<endl;
return 0;
}
第二题:不同子序列
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
分析::这道题相对于上面题目来说要复杂一些,因为当s[i-1]与t[j-1]相等时,dp[i][j]可以有两部分组成。
一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。
一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]。
所以当s[i - 1] 与 t[j - 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]
所以递推公式为:dp[i][j] = dp[i - 1][j];
#include <iostream>
#include <vector>
using namespace std;
int numDistinct(string &s,string &t)
{
vector<vector<int>>dp(s.size()+1,vector<int>(t.size()+1),0);
for(int i=0;i<s.size();++i)
dp[i][0]=1;
for(int j=1;j<t.size();++j)
dp[0][j]=0;
for(int i=1;i<=s.size();++i)
{
for(int j=1;j<=t.size();++j)
{
if(s[i-1]==t[j-1])
dp[i][j]=dp[i-1][j-1]+dp[i-1][j];//谁多谁减1;
else
dp[i][j]=dp[i-1][j];
}
}
return dp[s.size()][t.size()];
}
int main()
{
string s="bagg";
string t="bag";
int ret=numDistinct(s,t);
cout<<"ret="<<ret<<endl;
return 0;
}
第三题:两个字符串的删除操作
给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
示例:
输入: "sea", "eat"
输出: 2 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea".
分析:这道题相对上面2道题来说看起来非常复杂,为什么呢?因为前两道题只需要删除其中一个数组的元素就可以了,但是这道题就得对两个数组的元素进行操作了。
不过这道题是求最小步数,那么只需要把两个字符串的数量加起来-把最大步数*2=最小步数
以下代码我用s和t表示,这样写起来会省事一些。
#include <iostream>
#include <vector>
using namespace std;
int minDistance(string &s,string &t)
{
vector<vector<int>>dp(s.size()+1,vector<int>(t.size()+1));
for(int i=1;i<=s.size();++i)
{
for(int j=1;j<=t.size();++j)
{
if(s[i-1]==t[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 s.size()+t.size()-dp[s.size()][t.size()]*2;//用两个字符串的总长度减去两个最长公共子序列的长度就是删除的最少步数。
}
int main()
{
string s="sea";
string t="eat";
int val=minDistance(s,t);
cout<<"val="<<val<<endl;
return 0;
}
第四题:编辑距离
给你两个单词 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')。
分析:
- if (word1[i - 1] == word2[j - 1])
- 不操作
- if (word1[i - 1] != word2[j - 1])
- 增
- 删
- 换
也就是如上四种情况。
if (word1[i - 1] == word2[j - 1]) 那么说明不用任何编辑,dp[i][j] 就应该是 dp[i - 1][j - 1],即dp[i][j] = dp[i - 1][j - 1];
if (word1[i - 1] != word2[j - 1])
操作一:word1增加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-2为结尾的word1 与 i-1为结尾的word2的最近编辑距离 加上一个增加元素的操作。
即 dp[i][j] = dp[i - 1][j] + 1;
操作二:word2添加一个元素,使其word1[i - 1]与word2[j - 1]相同,那么就是以下标i-1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个增加元素的操作。
即 dp[i][j] = dp[i][j - 1] + 1;
操作三:替换元素,word1替换word1[i - 1],使其与word2[j - 1]相同,此时不用增加元素,那么以下标i-2为结尾的word1 与 j-2为结尾的word2的最近编辑距离 加上一个替换元素的操作。
即 dp[i][j] = dp[i - 1][j - 1] + 1;
综上,当 if (word1[i - 1] != word2[j - 1]) 时取最小的,即:dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;
所以代码如下:
#include <iostream>
#include <vector>
using namespace std;
int minDistance(string &word1,string &word2)
{
vector<vector<int>>dp(word1.size()+1,vector<int>(word2.size()+1));
for(int i=0;i<word1.size();++i)
dp[i][0]=i;
for(int j=0;j<word2.size();++j)
dp[0][j]=j;
for(int i=1;i<=word1.size();++i)
{
for(int j=1;j<=word2.size();++j)
{
if(word1[i-1]==word2[j-1])
{
dp[i][j]=dp[i-1][j-1];
}
else
{
dp[i][j]=min({dp[i-1][j],dp[i][j-1],dp[i-1][j-1]})+1;
}
}
}
return dp[word1.size()][word2.size()];
}
int main()
{
string s="horse";
string t="ros";
int val=minDistance(s,t);
cout<<"val="<<val<<endl;
return 0;
}
大家理解了以后可以自己写一下代码,在自己的机器上去试试。