编辑距离序列问题算是动态规划问题中的一个小分支,这里单独写一篇文章介绍。至于动态规划基础问题和详细的处理步骤我在我的另一篇文章中详细介绍过。具体解决步骤请移步观看——动态规划基础篇。如果想了解01背包问题和滚动数组相关内容请移步观看——动态规划——01背包问题。
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
提示:
dp[i][j]:以s.charAt(i-1)结尾的字符串s和以t.charAt(j-1)结尾的字符串t的相同子序列的长度为dp[i][j]。
大家看到这里可能会疑惑,这里不是要判断s 是否为 t 的子序列吗?为什么要将dp数组含义定位相同子序列长度?大家可以这样理解,如果递推结束后,最终结果dp[s.length()][t.length()]=s.length(),不就意味着s是t的子序列。
在确定递推公式的时候,首先要考虑如下两种操作,整理如下:
if(s.charAt(i-1)==t.charAt(j-1))
if(s.charAt(i-1)!=t.charAt(j-1))
如果s.charAt(i-1)==t.charAt(j-1),s.charAt(i-1)和t.charAt(j-1)也可以作为相同子序列中的一个字符,所以dp[i][j]=dp[i-1][j-1]+1;
如果s.charAt(i-1)!=t.charAt(j-1),我们可以删除t.charAt(j-1),那么此时以s.charAt(i-1)结尾的字符串s和以t.charAt(j-1)结尾的字符串t的相同子序列的长度显然等于s.charAt(i-1)结尾的字符串s和以t.charAt(j-2)结尾的字符串t的相同子序列的长度,因为我们已经把t.charAt(j-1)删掉了,所以dp[i][j]=dp[i][j-1]。
综上分析,递推公式如下:
if(s.charAt(i-1)==t.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=dp[i][j-1];
}
从递推公式可以看出,我们需要初始化dp[i][0]和dp[0][j],显然dp[i][0]=0,dp[0][j]=0;
class Solution {
public boolean isSubsequence(String s, String t) {
int len1=s.length(),len2=t.length();
int[][] dp=new int[len1+1][len2+1];
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(s.charAt(i-1)==t.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=dp[i][j-1];
}
}
}
return dp[len1][len2]==len1;
}
}
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
题目数据保证答案符合 32 位带符号整数范围。
提示:
dp[i][j]:以s.charAt(i-1)为结尾的s⼦序列中出现以t.charAt(j-1)为结尾的t的个数为dp[i][j]。
在确定递推公式的时候,首先要考虑如下两种操作,整理如下:
if(s.charAt(i-1)==t.charAt(j-1))
if(s.charAt(i-1)!=t.charAt(j-1))
如果s.charAt(i-1)==t.charAt(j-1),我们可以选择s.charAt(i-1)加入匹配,那么dp[i][j]=dp[i-1][j-1];也可以选择不让s.charAt(i-1)加入匹配,那么dp[i][j]=dp[i-1][j],例如s=“bagg”,t=“bag”,s.charAt(3)和s.charAt(2)是相同的我们选择s.charAt(3)可以不选择也可以,所以最终dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
如果s.charAt(i-1)!=t.charAt(j-1),dp[i][j]=dp[i-1][j]。
综上分析,递推公式如下:
if(s.charAt(i-1)==t.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
}else{
dp[i][j]=dp[i-1][j];
}
从递推公式可以看出,我们需要初始化dp[i][0]和dp[0][j];
dp[i][0]的含义是以s.charAt(i-1)结尾的字符串s子序列中出现空串的个数,很显然我们只需要把s字符串中所有的字符都删除即可得到空串,而且只有这一种可能,即dp[i][0]=1;
dp[0[][j]:的含义是空串的子序列中出现以t.charAt(j-1)结尾的字符串t,显然dp[0][j]=0。
class Solution {
public int numDistinct(String s, String t) {
int len1=s.length(),len2=t.length();
int[][] dp=new int[len1+1][len2+1];
for(int i=0;i<=len1;i++) dp[i][0]=1;
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(s.charAt(i-1)==t.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
}else{
dp[i][j]=dp[i-1][j];
}
}
}
return dp[len1][len2];
}
}
class Solution {
public int numDistinct(String s, String t) {
int len1=s.length(),len2=t.length();
int[] dp=new int[len2+1];
dp[0]=1;
for(int i=0;i<len1;i++){
for(int j=len2;j>0;j--){
if(s.charAt(i)==t.charAt(j-1)) dp[j]+=dp[j-1];
}
}
return dp[len2];
}
}
给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数。
每步 可以删除任意一个字符串中的一个字符。
提示:
dp[i][j]:以word1.charAt(i-1)结尾的字符串word1,以word2.charAt(j-1)结尾的字符串word2,使得word1和word2相同的最小步数为dp[i][j]。
在确定递推公式的时候,首先要考虑如下两种操作,整理如下:
if(word1.charAt(i-1)==word2charAt(j-1))
if(word1.charAt(i-1)!=word2charAt(j-1))
如果word1.charAt(i-1)==word2charAt(j-1),我们显然不需要做任何操作,即dp[i][j]=dp[i-1][j-1];
如果word1.charAt(i-1)!=word2charAt(j-1),我们可以选择删除word1.charAt(i-1),则dp[i][j]=dp[i-1][j]+1,也可以选择删除word2.charAt(j-1),则dp[i][j]=dp[i][j-1]+1,我们只需要选择步数最小的即可,即dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+1;
综上分析,递推公式如下:
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
从递推公式可以看出,我们需要初始化dp[i][0]和dp[0][j];
dp[i][0]的含义是word1.charAt(i-1)结尾的字符串word1和空串相同所需最小步数,所以我们需要将word1中所有的字符删去,即dp[i][0]=i;
同理,dp[0][j]=j。
class Solution {
public int minDistance(String word1, String word2) {
int len1=word1.length(),len2=word2.length();
int[][] dp=new int[len1+1][len2+1];
for(int i=1;i<=len1;i++) dp[i][0]=i;
for(int j=1;j<=len2;j++) dp[0][j]=j;
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+1;
}
}
}
return dp[len1][len2];
}
}
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
提示:
dp[i][j]:以word1.charAt(i-1)结尾的字符串word1,以word2.charAt(j-1)结尾的字符串word2,将 word1 转换成 word2 所使用的最少操作数。
在确定递推公式的时候,首先要考虑如下两种操作,整理如下:
if(word1.charAt(i-1)==word2charAt(j-1))
if(word1.charAt(i-1)!=word2charAt(j-1))
如果word1.charAt(i-1)==word2charAt(j-1),我们显然不需要做任何操作,即dp[i][j]=dp[i-1][j-1];
如果word1.charAt(i-1)!=word2charAt(j-1),我们有三种选择
综上分析,递推公式如下:
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j]=Math.min(Math.min(dp[i-1][j],dp[i][j-1),dp[i-1][j-1])+1;
}
从递推公式可以看出,我们需要初始化dp[i][0]和dp[0][j];
dp[i][0]的含义是word1.charAt(i-1)结尾的字符串word1转换为空串所需最小操作数,所以我们需要将word1中所有的字符删去,即dp[i][0]=i;
同理,dp[0][j]=j。
class Solution {
public int minDistance(String word1, String word2) {
int len1=word1.length(),len2=word2.length();
int[][] dp=new int[len1+1][len2+1];
for(int i=0;i<=len1;i++) dp[i][0]=i;
for(int j=1;j<=len2;j++) dp[0][j]=j;
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j]=Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;
}
}
}
return dp[len1][len2];
}
}