统计不同子序列出现的个数

统计不同子序列出现的个数

在本文中,我们将讨论如何解决一个有趣的问题:给定两个字符串st,我们需要统计ts的子序列中出现的个数。最终的结果需要对 1 0 9 + 7 10^9 + 7 109+7 取模。

问题描述

我们被要求统计字符串t在字符串s的子序列中出现的次数。这意味着我们需要在字符串s中找到所有可能的子序列,这些子序列可以通过删除某些字符来获得,并且这些子序列与字符串t相等。

动态规划的思路

为了解决这个问题,我们可以使用动态规划。我们定义一个二维数组 dp,其中 dp[i][j] 表示字符串 t 的前 i 个字符在字符串 s 的前 j 个字符的子序列中出现的个数。

接下来,我们可以考虑 dp[i][j] 的计算方式:

  • 如果 t[i-1] == s[j-1],那么有两种情况:
    • 我们可以选择不使用 s[j-1],也就是说 s[j-1] 不贡献到子序列中,那么 dp[i][j] = dp[i][j-1]
    • 我们可以选择使用 s[j-1],也就是说 s[j-1] 贡献到子序列中,那么 dp[i][j] = dp[i-1][j-1]
  • 如果 t[i-1] != s[j-1],那么 s[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)

示例

让我们使用示例来说明上述思路。

示例 1

s = "rabbbit"
t = "rabbit"

首先,我们创建一个二维数组 dp,并初始化如下:

   r a b b b i t
r  1 1 1 1 1 1 1
a  0
b  0
b  0
i  0
t  0

现在我们按照状态转移方程填充 dp 数组:

  1. dp[1][1]:考虑字符 ‘r’ 和 ‘r’。它们相等,所以 dp[1][1] = dp[0][0] = 1

  2. dp[1][2]:考虑字符 ‘r’ 和 ‘ra’。它们不相等,所以 dp[1][2] = dp[1][1] = 1

  3. dp[1][3]:考虑字符 ‘r’ 和 ‘rab’。它们不相等,所以 dp[1][3] = dp[1][2] = 1

  4. dp[1][4]:考虑字符 ‘r’ 和 ‘rabb’。它们不相等,所以 dp[1][4] = dp[1][3] = 1

  5. dp[1][5]:考虑字符 ‘r’ 和 ‘rabbb’。它们不相等,所以 dp[1][5] = dp[1][4] = 1

  6. dp[1][6]:考虑字符 ‘r’ 和 ‘rabbbi’。它们不相等,所以 dp[1][6] = dp[1][5] = 1

  7. dp[1][7]:考虑字符 ‘r’ 和 ‘rabbbit’。它们不相等,所以 dp[1][7] = dp[1][6] = 1

现在,我们填充第一行,然后按照相同的方式填充其余行。

最终,dp[6][7] 表示字符串 “rabbit” 在字符串 “rabbbit” 的子序列中出现的次数。结果为 dp[6][7] = 3

示例 2

s = "babgbag"
t = "bag"

我们执行类似的动态规划过程,填充 dp 数组。

最终,dp[3][7] 表示字符串 “bag” 在字符串 “babgbag” 的子序列中出现的次数。结果为 dp[3][7] = 5

Java 代码实现

现在,让我们将上述思路转化为 Java 代码:

class Solution {
    public int numDistinct(String s, String t) {
        int m = s.length();
        int n = t.length();
        int[][] dp = new int[n + 1][m + 1];

        // Initialize the first row with 1's
        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];
                if (t.charAt(i - 1) == s.charAt(j - 1)) {
                    dp[i][j] += dp[i - 1][j - 1];
                }
            }
        }

        return dp[n][m];
    }
}

这段代码实现了上述动态规划思路,并返回了ts的子序列中出现的次数。

你可能感兴趣的:(初学篇,代理模式,力扣)