• [LeetCode] 115. Distinct Subsequences 不同的子序列


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

    A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not).

    Example 1:

    Input: S = "rabbbit", T = "rabbit"
    Output: 3
    Explanation:
    
    As shown below, there are 3 ways you can generate "rabbit" from S.
    (The caret symbol ^ means the chosen letters)
    
    rabbbit
    ^^^^ ^^
    rabbbit
    ^^ ^^^^
    rabbbit
    ^^^ ^^^
    

    Example 2:

    Input: S = "babgbag", T = "bag"
    Output: 5
    Explanation:
    
    As shown below, there are 5 ways you can generate "bag" from S.
    (The caret symbol ^ means the chosen letters)
    
    babgbag
    ^^ ^
    babgbag
    ^^    ^
    babgbag
    ^    ^^
    babgbag
      ^  ^^
    babgbag
        ^^^

    看到有关字符串的子序列或者配准类的问题,首先应该考虑的就是用动态规划 Dynamic Programming 来求解,这个应成为条件反射。而所有 DP 问题的核心就是找出状态转移方程,想这道题就是递推一个二维的 dp 数组,其中 dp[i][j] 表示s中范围是 [0, i] 的子串中能组成t中范围是 [0, j] 的子串的子序列的个数。下面我们从题目中给的例子来分析,这个二维 dp 数组应为:

      Ø r a b b b i t
    Ø 1 1 1 1 1 1 1 1
    r 0 1 1 1 1 1 1 1
    a 0 0 1 1 1 1 1 1
    b 0 0 0 1 2 3 3 3
    b 0 0 0 0 1 3 3 3
    i 0 0 0 0 0 0 3 3
    t 0 0 0 0 0 0 0 3 

    首先,若原字符串和子序列都为空时,返回1,因为空串也是空串的一个子序列。若原字符串不为空,而子序列为空,也返回1,因为空串也是任意字符串的一个子序列。而当原字符串为空,子序列不为空时,返回0,因为非空字符串不能当空字符串的子序列。理清这些,二维数组 dp 的边缘便可以初始化了,下面只要找出状态转移方程,就可以更新整个 dp 数组了。我们通过观察上面的二维数组可以发现,当更新到 dp[i][j] 时,dp[i][j] >= dp[i][j - 1] 总是成立,再进一步观察发现,当 T[i - 1] == S[j - 1] 时,dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1],若不等, dp[i][j] = dp[i][j - 1],所以,综合以上,递推式为:

    dp[i][j] = dp[i][j - 1] + (T[i - 1] == S[j - 1] ? dp[i - 1][j - 1] : 0)

    根据以上分析,可以写出代码如下:

    class Solution {
    public:
        int numDistinct(string s, string t) {
            int m = s.size(), n = t.size();
            vector<vector<long>> dp(n + 1, vector<long>(m + 1));
            for (int j = 0; j <= m; ++j) dp[0][j] = 1;
            for (int i = 1; i <= n; ++i) {
                for (int j = 1; j <= m; ++j) {
                    dp[i][j] = dp[i][j - 1] + (t[i - 1] == s[j - 1] ? dp[i - 1][j - 1] : 0);
                }
            }
            return dp[n][m];
        }
    };

    Github 同步地址:

    https://github.com/grandyang/leetcode/issues/115

    参考资料:

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

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

    https://leetcode.com/problems/distinct-subsequences/discuss/37412/Any-better-solution-that-takes-less-than-O(n2)-space-while-in-O(n2)-time

    LeetCode All in One 题目讲解汇总(持续更新中...)

  • 相关阅读:
    stm8s103 EEPROM烧程序时能否保留
    NEC芯片特别说明
    pic中断特别说明
    删除排序链表中的重复元素 II
    被围绕的区域
    计数二进制子串
    简单工厂模式
    打家劫舍 II
    打家劫舍
    相同的树
  • 原文地址:https://www.cnblogs.com/grandyang/p/4294105.html
Copyright © 2020-2023  润新知