力扣每日一题115:不同的子序列

题目

困难

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

示例 1:

复制代码
输入:s = "rabbbit", t = "rabbit"
输出:3
解释:
如下所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。
++rabb++b++it++
++ra++b++bbit++
++rab++b++bit++

示例 2:

复制代码
输入:s = "babgbag", t = "bag"
输出:5
解释:
如下所示, 有 5 种可以从 s 中得到 "bag" 的方案。 
++ba++b**g**bag
++ba++bgba++g++
**b**abgb++ag++
ba**b**gb**ag**
babg++bag++

提示:

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

面试中遇到过这道题?

1/5

通过次数

177K

提交次数

340.8K

通过率

51.9%

思路

注意!这是一道hard题!

看到xxx序列在xxx序列中xxx的个数、序列的最长xxx子序列、最长xxx串,最短xxx串这些题目,一眼就想到动态规划。

序列t在序列s的子序列中出现的次数,出现了两个序列的匹配,毫无疑问是二维dp,我们采用由前向后dp的思路,dp[i][j]来表示 t[1:i] 在 s[1:j] 的子序列中出现的次数。注意!由于动态规划一般要对边界值进行讨论,dp的下标从[1][1]开始,[0][......]和[......][0]用来做边界值的讨论。

对于这种两个字符串的动态规划,一般都是讨论 [i] 和 [j] 各自对应的字符是否相等来分类。

状态转移

1、如果t[i-1]!=s[j-1],也就是 i 对应的字符和 j 对应的字符不相等的情况。

要实现 t[1:i] 和 s[1:j] 的匹配,因为两部分切片的最后一个字符不相等,所有只能祈求 t[1:i] 能不能和 s[1:j-1] 能不能匹配。dp[i][j]=dp[i][j-1]。

2、t[i-1]==s[j-1]

两部分切片的最后一个字符相等,所以我们既可以选择两种匹配方法

a、t[i-1] 和 s[j-1] 匹配完成后,t[1:i-1]和t[1:j-1]匹配,这一部分的方案数量是 dp[i-1][j-1]

b、t[i-1]不和s[j-1]匹配,这时就和t[i-1]!=s[j-1]一样,这一部分方案数量就是 dp[i][j-1]

dp[i][j]=dp[i-1][j-1]+dp[i][j-1]

边界情况的讨论。

状态转移方程中,我们要用到dp[i-1][j-1],而我们的状态转移的循环是

复制代码
        for(int i=1;i<=m;i++){
            for(int j=i;j<=n;j++){
                if(t[i-1]!=s[j-1]){
                    dp[i][j]=dp[i][j-1];
                }else{//t[i-1]和s[j-1]匹配+t[i-1]不和s[j-1]匹配
                    dp[i][j]=dp[i-1][j-1]+dp[i][j-1];
                }
            }
        }

m是t串的长度,n是s串的长度。

从循环上看,j<i||i==0 这一部分是没有出现在循环里的,但是i-1和j-1能遍历到,所以我们必然要讨论。

i=0时,代表切片为空串,空串是任何串的子串,所以这一部分的初值为1。

j<i时,s串长度小于t串,这是不可能是子串,所以这一部分初值为0。

复制代码
        vector<vector<unsigned long long>> dp(m+1,vector<unsigned long long>(n+1,0));
        //边界情况-->空字符串是任何字符串的子序列
        for(int i=0;i<=n;i++){
            dp[0][i]=1;
        }

代码(注意数据范围和取模)

cpp 复制代码
class Solution {
public:
    const int mod=1e9+7;
    int numDistinct(string s, string t) {
        int n=s.length();
        int m=t.length();
        if(n<m) return 0;
        //dp[i][j]-->t[1-i]在s[1-j]中出现的次数()
        //防止边界条件判断,从[1][1]开始算
        //long long都会超出数据范围
        vector<vector<unsigned long long>> dp(m+1,vector<unsigned long long>(n+1,0));
        //边界情况-->空字符串是任何字符串的子序列
        for(int i=0;i<=n;i++){
            dp[0][i]=1;
        }
        for(int i=1;i<=m;i++){
            for(int j=i;j<=n;j++){
                if(t[i-1]!=s[j-1]){
                    dp[i][j]=dp[i][j-1];
                }else{//t[i-1]和s[j-1]匹配+t[i-1]不和s[j-1]匹配
                    dp[i][j]=dp[i-1][j-1]+dp[i][j-1];
                }
            }
        }
        int ans=dp[m][n]%mod;
        return ans;
    }
};
相关推荐
Java致死2 小时前
设计模式Java
java·开发语言·设计模式
源码方舟2 小时前
SpringBoot + Shiro + JWT 实现认证与授权完整方案实现
java·spring boot·后端
金融小师妹4 小时前
应用BERT-GCN跨模态情绪分析:贸易缓和与金价波动的AI归因
大数据·人工智能·算法
广州智造4 小时前
OptiStruct实例:3D实体转子分析
数据库·人工智能·算法·机器学习·数学建模·3d·性能优化
2401_cf5 小时前
为什么hadoop不用Java的序列化?
java·hadoop·eclipse
帮帮志5 小时前
idea整合maven环境配置
java·maven·intellij-idea
LuckyTHP5 小时前
java 使用zxing生成条形码(可自定义文字位置、边框样式)
java·开发语言·python
Trent19856 小时前
影楼精修-肤色统一算法解析
图像处理·人工智能·算法·计算机视觉
feifeigo1236 小时前
高光谱遥感图像处理之数据分类的fcm算法
图像处理·算法·分类
北上ing7 小时前
算法练习:19.JZ29 顺时针打印矩阵
算法·leetcode·矩阵