题目

https://leetcode.com/problems/distinct-subsequences/

题解

方法1:递归(超时)

这种解法比较容易理解,时间复杂度没算出来,但肯定不是 O(m*n)。提交之后超时了。

2021-9-20 09:33:04 补充:根据左神的“从暴力递归到动态规划”,可以给此方法加傻缓存,即可得到动态规划解法。

class Solution {public int numDistinct(String s, String t) {char[] ss = s.toCharArray();char[] tt = t.toCharArray();int count = count(ss, tt, 0, 0);return count;}public int count(char[] ss, char[] tt, int sBegin, int tBegin) {if (tBegin == tt.length) return 1;if (sBegin == ss.length) return 0;int cnt = 0;while (sBegin != ss.length) {if (ss[sBegin] == tt[tBegin]) {cnt += count(ss, tt, sBegin + 1, tBegin + 1); // 固定当前字母并调子过程}// 不固定当前字母继续搜索sBegin++;}// System.out.println("cnt2=" + cnt);return cnt;}
}

方法2:动态规划

参考:https://leetcode.com/problems/distinct-subsequences/discuss/37327/Easy-to-understand-DP-in-Java

The idea is the following:

  • we will build an array mem where mem[i+1][j+1] means that S[0..j] contains T[0..i] that many times as distinct subsequences. Therefor the result will be mem[T.length()][S.length()].
  • we can build this array rows-by-rows:
  • the first row must be filled with 1. That’s because the empty string is a subsequence of any string but only 1 time. So mem[0][j] = 1 for every j. So with this we not only make our lives easier, but we also return correct value if T is an empty string.
  • the first column of every rows except the first must be 0. This is because an empty string cannot contain a non-empty string as a substring – the very first item of the array: mem[0][0] = 1, because an empty string contains the empty string 1 time.

So the matrix looks like this:

  S 0123....j
T +----------+|1111111111|
0 |0         |
1 |0         |
2 |0         |
. |0         |
. |0         |
i |0         |

From here we can easily fill the whole grid: for each (x, y), we check if S[x] == T[y] we add the previous item and the previous item in the previous row, otherwise we copy the previous item in the same row. The reason is simple:

  • if the current character in S doesn’t equal to current character T, then we have the same number of distinct subsequences as we had without the new character.
  • if the current character in S equal to the current character T, then the distinct number of subsequences = the same number we had before plus the distinct number of subsequences we had with less longer T and less longer S.

An example:
S: [acdabefbc] and T: [ab]

first we check with a:

           *  *S = [acdabefbc]
mem[1] = [0111222222]

then we check with ab:

               *  * S = [acdabefbc]
mem[1] = [0111222222]
mem[2] = [0000022244]

And the result is 4, as the distinct subsequences are:

      S = [a   b    ]S = [a      b ]S = [   ab    ]S = [   a   b ]

See the code in Java:

public int numDistinct(String S, String T) {// array creationint[][] mem = new int[T.length()+1][S.length()+1];// filling the first row: with 1sfor(int j=0; j<=S.length(); j++) {mem[0][j] = 1;}// the first column is 0 by default in every other rows but the first, which we need.for(int i=0; i<T.length(); i++) {for(int j=0; j<S.length(); j++) {if(T.charAt(i) == S.charAt(j)) {mem[i+1][j+1] = mem[i][j] + mem[i+1][j];} else {mem[i+1][j+1] = mem[i+1][j];}}}return mem[T.length()][S.length()];
}

Sure let’s consider the same example as above: S = [acdabefbc], T = [ab]

               *  * S = [acdabefbc]
mem[1] = [0111222222]
mem[2] = [00000222_ ]

Imagine that we are filling the gap at _. That means i=1, so T[i] = b and j=7, so S[j] = b.

We’re looking for mem[i+1][j+1], which is the place for _. Currently we know that at this position we have 2 as before, because mem[1][7] = 2, which is the position ABOVE and LEFT to _. Also we know that so far we had 2 subsequences before (namely AcdaBef and acdABef – highlighted with uppercase) because mem[2][7] = 2, which is LEFT to _. So having this new b would increase the number of subsequences (currently 2) with a number of 2, because it can be matched with the 2 as we saw before. That’s why if T[i] == S[j] then mem[i+1][j+1] := mem[i][j] + mem[i+1][j]. So _ will be 4.

总结一下就是:当前 i, j 位置不同子序列数量 = 包含当前字母之前的 [i-1][j-1] 位置已有的不同子序列数量(之前缺当前字母,所以不是完整的子序列,但由于当前字母相同,所以构成并增加了新的可能情况) + 如果当前字母不相同的话 [i][j-1] 位置已经有的的子序列数量(被继承过来作为累加)

I hope this helped.

自己实现了一遍:

class Solution {public int numDistinct(String s, String t) {int[][] dp = new int[t.length() + 1][s.length() + 1]; // dp[i][j]表示t[:i]在s[:j]范围内的不同子序列数量for (int i = 0; i < s.length() + 1; i++) {dp[0][i] = 1; // "" 是任何序列的子序列}for (int i = 0; i < t.length(); i++) {dp[i + 1][0] = 0; // "" 不包含任何子序列for (int j = 0; j < s.length(); j++) {if (s.charAt(j) == t.charAt(i)) {// dp[i+1][j+1] = 不选定当前字母时继承前面已有的子序列数量 + 选定当前字母时与前一个不包含此字母的子序列拼接而产生的新的子序列数量dp[i + 1][j + 1] = dp[i + 1][j] + dp[i][j];}else {dp[i + 1][j + 1] = dp[i + 1][j];}}}return dp[t.length()][s.length()];}
}

leetcode 115. Distinct Subsequences Hard | 115. 不同的子序列(动态规划)相关推荐

  1. 【重点!DP】LeetCode 115. Distinct Subsequences

    LeetCode 115. Distinct Subsequences Solution1: 不会做.. 参考网址:https://www.youtube.com/watch?v=mPqqXh8XvW ...

  2. LeetCode:115. Distinct Subsequences

    题目 Given a string S and a string T, count the number of distinct subsequences of S which equals T. A ...

  3. 【Leetcode】115. Distinct Subsequences

    又是一道DP的题目,个人感觉DP的题目比较难,主要因为:(1)DP的难点是寻找子问题,如果找到很好的子问题,那么就可以瞬间搞定.(2)通常也会带有一点backtracking的思想,有时候总是优先想到 ...

  4. lc 115. Distinct Subsequences

    https://leetcode.com/problems/distinct-subsequences/description/ 求串s中有几个字串t? 开始看成了s和t有几个公共字串,想来也是dp. ...

  5. Distinct Subsequences@LeetCode

    Distinct Subsequences 动态规划题.先用二维动态规划的思路解释下:设match是动态规划表,其中match[i][j]表示S.substring(0, i)对T.substring ...

  6. [LeetCode]Distinct Subsequences,解题报告

    题目 Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequen ...

  7. 【leetcode】940. Distinct Subsequences II

    题目如下: Given a string S, count the number of distinct, non-empty subsequences of S . Since the result ...

  8. LeetCode刷题:741. Cherry Pickup —摘樱桃 动态规划

    LeetCode刷题:741. Cherry Pickup -摘樱桃 动态规划 原题链接:https://leetcode.com/problems/cherry-pickup/ In a N x N ...

  9. 119. Leetcode 115. 不同的子序列 (动态规划-子序列问题)

    步骤一.确定状态: 确定dp数组及下标含义 dp[i][j]: 长度为i的s子串删除字符得到长度为j的t的子串的方法个数 步骤二.推断状态方程: 这种情况一般都会分字符相等和不相等的情况.如果当前的字 ...

最新文章

  1. 30 个实例详解 TOP 命令!
  2. c构造函数和析构函数_C ++构造函数和析构函数| 查找输出程序| 套装1
  3. hive 如何将数组转成字符串_教你如何将Power Logic的原理图转成Orcad的原理图
  4. pythongui做计算器_python GUI之简易计算器
  5. 用Eclipse 统计代码行数小技巧
  6. 文档开发工具调研总结
  7. AssetPostprocessor.OnPreprocessModel
  8. 用c语言编写金山打字游戏,c#实现简单金山打字小游戏(源码)
  9. 前端开发【WEUI框架H5网页开发】—— WEUI+Node.js+fis3 项目开发
  10. 将Wi-Fi生成二维码
  11. Java识别获取pdf中文字信息
  12. Longhorn,企业级云原生容器分布式存储 - 监控(Prometheus+AlertManager+Grafana)
  13. 前后端分离实现在线音乐网站-springboot+vue
  14. 微信小程序获取输入框(input)内容
  15. 用计算机做表格的超链接,【答疑】用Excel表格如何添加一个链接?如何将一个超链接添加Excel表格里? - 视频教程线上学...
  16. 生死单元技术!如何在abaqus焊接仿真中的应用
  17. js 将空格和换行符转换成HTML标签
  18. ps基础学习:图案图章工具画布填充
  19. Linux系统源码安装PHP(超详细)
  20. 浅谈模糊PI调节器的优缺点

热门文章

  1. java 静态对象赋值_基于Java class对象说明、Java 静态变量声明和赋值说明(详解)...
  2. HDU1756(判断点在多边形内外)
  3. python selenium 鼠标移动到指定元素,并点击对应的元素
  4. Java集合框架:总结
  5. 读取 classpath 资源
  6. Go 程序是怎样跑起来的
  7. 由“深”至“广”,探索2022音视频技术的无限可能
  8. 【线上分享】下一代互联网通讯协议:QUIC
  9. LiveVideoStackCon 一次全新的尝试,错过了就是一辈子
  10. Google开源框架AutoFlip 实现视频智能剪裁