【LeetCode】115. 不同的子序列 - Go语言中文社区

【LeetCode】115. 不同的子序列


在这里插入图片描述
规定 :
(1)S(0, i-1) 表示 S 的前 i 个字符组成的字符串,因此基数为 0,所以最后一个字符即第 i-1 个。

(2)S[i-1] 即表示 S 的第 i-1 个字符。


使用动态规划来处理,则有 dp[i][j] 代表 S(0, i-1)T(0, j-1) 对应的解。

对于 dp[i][j] 有:

  1. S[i-1]T[j-1] 不相等时,则 S 的第 i-1 个字符肯定是要删除的,删除了 S 的第 i-1 个字符,就等价于求 S(0, i-2)T(0, j-1) 的解,因此 dp[i][j] 就等价于 dp[i-1][j]

    也可以反过来想,对于 T(j-1) ,当 S[i-1]T[j-1] 不相等时,对于 S 的前 i-1 个字符(即 S(0, i-2)),再加上第 i-1 个字符,是不会影响到关于 T(j-1) 的结果的。

  2. S[i-1]T[j-1] 相等 时,S 的第 i-1 个字符也是要删除的,但是当其删除时,就会有两种情况:

    抵消T[j-1](即抵消掉 T 的第 j-1 个字符,则剩下 T 的前 (j-1) 个字符,此时最后一个字符变为第 T[j-2] 个了),此时就对应于 dp[i-1][j-1]

    或者不抵消(即 T 的第 j-1 个字符还在,则剩下的依然是 T 的前 j 个字符),此时对应于 dp[i-1][j]

    此时 dp[i][j] 为两种情况的和,即 dp[i][j] = dp[i-1][j-1] + dp[i-1][j]

public int numDistinct(String s, String t) {
    if (s == null || s.length() == 0) {
        return 0;
    }
    if (t == null || t.length() == 0) {
        return 1;
    }
    
    int sLen = s.length();
    int tLen = t.length();
    // 注意数组空间为 [sLen + 1][tLen + 1]
    int[][] dp = new int[sLen + 1][tLen + 1];
	
	// 初始值赋值
    for (int i = 0; i <= sLen; i++) {
        dp[i][0] = 1;
    }
    
    // 最终要求得 dp[sLen][tLen]
    for (int i = 1; i <= sLen; i++) {
        for (int j = 1; j <= tLen; 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[sLen][tLen];
}

注意,对于 dp[i][0],即 T 的字串长度为 0 的时候,有 dp[i][0] 为 1,即空字符串为 S 的子串只有一种可能,那就是删除 S 的所有字符。

而对于 dp[0][j] 不包括 dp[0][0](因为它属于 dp[i][0] 对应的情形 ),即 S 为空字符串,此时不管 T 多长,都无法通过删除 S 中的字符来变成 T,因此 dp[0][j] 为 0。

上述实现的空间复杂度为 O(sLen*tLen)


空间复杂度还可以优化,即使用一纬的辅助数组,此时重点就是对于 dp[i-1][j-1] 状态的保存,因为它在遍历的时候会被 dp[i][j-1] 给覆盖。

此时,就可以用一个临时变量,来保存被覆盖之前的 dp[i-1][j-1] 状态。

public int numDistinct(String s, String t) {
    if (s == null || s.length() == 0) {
        return 0;
    }
    if (t == null || t.length() == 0) {
        return 1;
    }
    int sLen = s.length();
    int tLen = t.length();
    // 使用一纬数组,此时 dp[j] 表示 t 的前 j 个字符
    int[] dp = new int[tLen + 1];
    dp[0] = 1;
    for (int i = 1; i <= sLen ; i++) {
        int pre = dp[0];
        dp[0] = 1;
        for (int j = 1; j <= tLen; j++) {
            // 用临时状态保存更新前的 dp[j],此时为 dp[i-1][j-1] 对应的状态
            int tmp = dp[j];
            if (s.charAt(i-1) == t.charAt(j-1)) {
                dp[j] = pre + tmp;
            } else {
                dp[j] = tmp;
            }
            // 将更新前的 dp[i] 赋值给 pre,用于遍历下一个元素
            pre = tmp;
        }
    }
    return dp[tLen];
}
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/OneDeveloper/article/details/100111943
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-02-25 02:25:58
  • 阅读 ( 915 )
  • 分类:算法

0 条评论

请先 登录 后评论

官方社群

GO教程

推荐文章

猜你喜欢