Leetcode No.115 不同的子序列(动态规划)
一、题目描述
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
题目数据保证答案符合 32 位带符号整数范围。
提示:
0 <= s.length, t.length <= 1000 s 和 t 由英文字母组成
二、解题思路
假设字符串 s 和 t 的长度分别为 m 和 n。如果 t 是 s 的子序列,则 s 的长度一定大于或等于 t 的长度,即只有当 m≥n 时,t 才可能是 s 的子序列。如果 m<n,则 t 一定不是 s 的子序列,因此直接返回 0。
当 m≥n 时,可以通过动态规划的方法计算在 s 的子序列中 t 出现的个数。
创建二维数组 dp,其中 dp[i][j] 表示在 s[i:]的子序列中 t[j:]出现的个数。
上述表示中,s[i:] 表示 s从下标 i 到末尾的子字符串,t[j:] 表示 t 从下标 j 到末尾的子字符串。
考虑动态规划的边界情况:
1、当 j=n时,t[j:] 为空字符串,由于空字符串是任何字符串的子序列,因此对任意0≤i≤m,有 dp[i][n]=1;
2、当 i=m且 j<n时,s[i:]为空字符串,t[j:] 为非空字符串,由于非空字符串不是空字符串的子序列,因此对任意0≤j<n,有dp[m][j]=0。
当 i<m 且 j<n 时,考虑 dp[i][j] 的计算:
1、当 s[i]=t[j]时,dp[i][j] 由两部分组成:
①如果 s[i] 和 t[j]匹配,则考虑 t[j+1:]作为 s[i+1:]的子序列,子序列数为 dp[i+1][j+1];
②如果 s[i]不和 t[j]匹配,则考虑 t[j:]作为 s[i+1:] 的子序列,子序列数为 dp[i+1][j]。
因此当 s[i]=t[j]时,有 dp[i][j]=dp[i+1][j+1]+dp[i+1][j]。
2、当 s[i] !=t[j] 时,s[i]不能和 t[j]匹配,因此只考虑 t[j:]作为 s[i+1:]的子序列,子序列数为dp[i+1][j]。
因此当 s[i] !=t[j] 时,有 dp[i][j]=dp[i+1][j]。
由此可以得到如下状态转移方程:
最终计算得到 dp[0][0] 即为在 s 的子序列中 t 出现的个数。
三、代码
class Solution2 {
public int numDistinct(String s, String t) {
int m = s.length(), n = t.length();
//如果t是s的子序列,则s的长度一定大于或等于t的长度,如果 m<n,则t一定不是s的子序列,因此直接返回 0。
if (m < n) {
return 0;
}
//s[i:] 表示 s从下标 i 到末尾的子字符串,t[j:] 表示 t 从下标 j 到末尾的子字符串。
//dp[i][j] 表示在 s[i:]的子序列中 t[j:]出现的个数,默认值全为0
int[][] dp = new int[m + 1][n + 1];
//当 i=m且 j<n时,s[i:]为空字符串,t[j:] 为非空字符串,由于非空字符串不是空字符串的子序列,因此对任意0≤j<n,有dp[m][j]=0。
//当 j=n时,t[j:] 为空字符串,由于空字符串是任何字符串的子序列,因此对任意0≤i≤m,有 dp[i][n]=1;
for (int i = 0; i <= m; i++) {
dp[i][n] = 1;
}
for (int i = m - 1; i >= 0; i--) {
char sChar = s.charAt(i);
for (int j = n - 1; j >= 0; j--) {
char tChar = t.charAt(j);
//当 s[i]=t[j]时
if (sChar == tChar) {
//①如果 s[i] 和 t[j]匹配,则考虑 t[j+1:]作为 s[i+1:]的子序列,子序列数为 dp[i+1][j+1];
//②如果 s[i]不和 t[j]匹配,则考虑 t[j:]作为 s[i+1:] 的子序列,子序列数为 dp[i+1][j]。
dp[i][j] = dp[i + 1][j + 1] + dp[i + 1][j];
} else {
//s[i]!=t[j] 时,s[i]不能和 t[j]匹配,因此只考虑 t[j:]作为 s[i+1:]的子序列,子序列数为dp[i+1][j]
dp[i][j] = dp[i + 1][j];
}
}
}
return dp[0][0];
}
public static void main(String[] args) {
Solution2 solution2=new Solution2();
System.out.println(solution2.numDistinct("rabbbit","rabbit"));
}
}
四、复杂度分析
时间复杂度:O(mn),其中 m 和 n 分别是字符串 s 和 t 的长度。二维数组 dp 有 m+1 行和 n+1 列,需要对dp 中的每个元素进行计算。
空间复杂度:O(mn),其中 m 和 n 分别是字符串 s 和 t 的长度。创建了 m+1 行 n+1列的二维数组 dp。
相关文章
- 金融服务领域的大数据:即时分析
- 影响大数据、机器学习和人工智能未来发展的8个因素
- 从0开始构建一个属于你自己的PHP框架
- 如何将Hadoop集成到工作流程中?这6个优秀实践必看
- SEO公司使用大数据优化其模型的5种方法
- 关于Web Workers你需要了解的七件事
- 深入理解HTTPS原理、过程与实践
- 增强分析:数据和分析的未来
- PHP协程实现过程详解
- AI专家:大数据知识图谱——实战经验总结
- 关于PHP的错误机制总结
- 利用数据分析量化协同过滤算法的两大常见难题
- 怎么做大数据工作流调度系统?大厂架构师一语点破!
- 2019大数据处理必备的十大工具,从Linux到架构师必修
- OpenCV中的KMeans算法介绍与应用
- 教大家如果搭建一套phpstorm+wamp+xdebug调试PHP的环境
- CentOS下三种PHP拓展安装方法
- Go语言HTTP Server源码分析
- Go语言HTTP Server源码分析
- 2017年4月编程语言排行榜:Hack首次进入前五十