题目描述:

给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数,结果需要对 10^9 + 7 取模。
image
image

提示:

1 <= s.length, t.length <= 1000
s 和 t 由英文字母组成

思路分析:

  1. 为什么可以用动态规划来解决这个题目呢?

    最优子结构:问题可以分解为更小的子问题。例如,如果我们已经知道了 s 中前 i 个字符和 t 中前 j 个字符的子序列数,我们可以利用这个信息来推导 s 中前 i+1 个字符和 t 中前 j+1 个字符的子序列数。

    重叠子问题:子问题的结果会被多次使用。比如,计算 s[0..i] 和 t[0..j] 的子序列数时,我们可能会多次计算相同的子问题,因此可以通过保存中间结果来避免重复计算。

  2. DP五部曲
    2.1 明确DP数组的含义
    一般来说DP的含义就是题目要求的东西,本题要求S中出现的T的子序列的数量,那么DP的含义就应该往这上面靠。同时DP的核心意思是记录子问题的答案来解决后续问题,所以对应这个题目,我们需要不断遍历子序列,就是不同的序列长度,这是子问题和区别,所以DP数组自然就是定义为:
    dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
    i-1和j-1是为了方便遍历和初始化。
    2.2递归函数
    反正做法就是循环遍历所有元素的所有情况,那么遍历过程中就有两种情况:
    1.当前两个元素相同
    如果当前两个字符串中的元素相同,那么dp[i][j]的值就等于没有这两个字符的值+不使用s中最后一个元素的情况。即:dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]。比如bagg和bag
    2.当前两个元素不相同
    这时候就是不要被比较的元素的情况,即:dp[i][j] = dp[i - 1][j]。

注意一个问题,就是对比两个字符串的时候总是把一个字符串作为基准字符串,空字符永远是某个元素的子串,所以第一行的元素都是1.

点击查看代码
func numDistinct(s string, t string) int {
    m,n:=len(t),len(s)
    dp:=make([][]int,m+1)
    for i:=range dp{
        dp[i] = make([]int, n+1)
    }
    for j := 0; j < n+1; j++ {
        dp[0][j] = 1
    }
    for i := 1; i < m+1; i++ {
        for j := 1; j < n+1; j++ {
            if t[i-1] == s[j-1] {
                dp[i][j] = dp[i-1][j-1]+dp[i][j-1]
            }else {
                dp[i][j] = dp[i][j-1]
            }
        }

    }
    return dp[m][n]
}