解题思路
-
理解问题:
- 我们需要在字符串
S中找到所有可能的子序列P的出现次数。 - 子序列
P中的字符在S中可以不连续,但顺序必须一致。
- 我们需要在字符串
-
数据结构选择:
- 由于我们需要动态地计算子序列的出现次数,可以使用动态规划(DP)来解决这个问题。
-
算法步骤:
-
创建一个二维数组
dp,其中dp[i][j]表示S的前i个字符中子序列P的前j个字符的出现次数。 -
初始化
dp[0][0] = 1,表示空字符串到空字符串的匹配次数为1。 -
遍历字符串
S的每个字符,并更新dp数组:- 如果
S[i-1] == P[j-1],则dp[i][j] = dp[i-1][j-1] + dp[i-1][j]。 - 否则,
dp[i][j] = dp[i-1][j]。
- 如果
-
最终,
dp[S.length()][P.length()]就是S中子序列P的出现次数。
-
-
取模操作:
- 由于结果需要对
10^9 + 7取模,我们在每次更新dp数组时都要进行取模操作。
- 由于结果需要对
public class Main {
public static int solution(String S, String P) {
int mod = 1000000007;
int n = S.length();
int m = P.length();
// dp[i][j] 表示 S 的前 i 个字符中子序列 P 的前 j 个字符的出现次数
int[][] dp = new int[n + 1][m + 1];
// 初始化:空字符串到空字符串的匹配次数为1
dp[0][0] = 1;
// 遍历 S 的每个字符
for (int i = 1; i <= n; i++) {
// 对于每个字符,初始化 dp[i][0] 为1,因为空字符串是任何字符串的子序列
dp[i][0] = 1;
for (int j = 1; j <= m; j++) {
// 如果 S[i-1] 和 P[j-1] 匹配
if (S.charAt(i - 1) == P.charAt(j - 1)) {
dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod;
} else {
// 如果不匹配,则继承前一个状态
dp[i][j] = dp[i - 1][j];
}
}
}
// 返回 S 中子序列 P 的出现次数
return dp[n][m];
}
public static void main(String[] args) {
System.out.println(solution("ABC", "A") == 1);
System.out.println(solution("AABCCD", "CCD") == 1);
System.out.println(solution("AABCCD", "C") == 2);
}
}
代码解释
-
初始化:
dp[0][0] = 1:空字符串到空字符串的匹配次数为1。dp[i][0] = 1:任何字符串的前i个字符中,空字符串的出现次数为1。
-
动态规划更新:
- 对于每个字符
S[i-1],我们检查它是否与P[j-1]匹配。 - 如果匹配,
dp[i][j]更新为dp[i-1][j-1] + dp[i-1][j],表示当前字符匹配的情况下,加上不匹配的情况。 - 如果不匹配,
dp[i][j]继承前一个状态dp[i-1][j]。
- 对于每个字符
-
取模操作:
- 在每次更新
dp数组时,都对结果取模10^9 + 7,以防止溢出。
- 在每次更新
-
返回结果:
- 最终,
dp[n][m]就是S中子序列P的出现次数。
- 最终,