问题
115.不同的子序列(力扣)
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
动态规划解题步骤
1、概述
建立二位数组dp[][],行表示串s[0...m-1],列表示串t[0...n-1],串在数组中从下标为[1]开始。
例:s = "rabbbit", t = "rabbit",如图:其中s[0]对应列dp[][1],t[0]对应行dp[1][]

约定:串t在串s子序列中出现的个数记为num[s,t];dp[i][j]的值表示t的子串t'[0...i-1]在s的子串s'[0...j-1]子序列中出现的个数,即num[s',t']。
2、确定状态
(1)最后一步
如图:

此时s的子串s'[0...m-2]与t的子串t[0...n-1]已经比对完毕,num[s',t]已经求得,num[s',t']表示串t'在s'子序列中出现的个数。
对比字符s[m-1]与t[n-1],若二者不相等,则num[s,t]=num[s',t]+0,即没有新增;若二者相等,则num[s,t]=num[s',t]+num[s',t'],仔细思考一下,此时多出来的个数为串t'在串s'子序列中出现的个数,因为串t的最后一个字符多了一个位置,便会增多num[s',t']种情况。
(2)子问题
原问题是求串s[0...m-1]子序列中串t[0...n-1]出现的个数,即求num[s,t];
子问题是求子串s'[0...j-1]子序列中子串t'[0...i]出现的个数,即求num[s',t']。
3、转移方程
根据上述约定,dp[i][j-1]=num[s',t],dp[i-1][j-1]=num[s',t']。
故比对到的字符如果不相等:dp[i][j]=dp[i][j-1]
如果相等:dp[i][j]=dp[i-1][j-1]+dp[i][j-1]
4、初始条件和边界情况
初始条件:数组第一行dp[0][]的值均为1
边界情况:列号>=行号,因为若s'.length()<t'.length(),则s'中必定不存在子序列t';遍历到最后一个字符时结束
5、计算顺序
按行优先遍历,即先计算t'[0...i]在串s子序列中出现的个数,再计算t''[0...i+1]在串s子序列中出现的个数。
时间和空间复杂度均为O(m*n)
代码:
class Solution {
public int numDistinct(String s, String t) {
int m=s.length(),n=t.length();
int[][] dp=new int[n+1][m+1];
for (int j=0;j<m;++j) dp[0][j]=1;
for (int i=1;i<=n;++i){
for (int j=i;j<=m;++j){
if(t.charAt(i-1)==s.charAt(j-1)) dp[i][j]=dp[i-1][j-1]+dp[i][j-1];
else dp[i][j]=dp[i][j-1];
}
}
return dp[n][m];
}
}
不足之处或优化建议希望能在评论区分享,一起学习!
这篇博客详细介绍了如何运用动态规划解决力扣题目115:不同子序列的问题。通过建立二维数组dp[][],根据字符串s和t的长度进行遍历,确定状态转移方程,最终计算出t在s的子序列中出现的次数。文章包含解题步骤、状态定义、转移方程、初始条件和边界情况,并给出了计算顺序及时间复杂度分析。
464

被折叠的 条评论
为什么被折叠?



