Leetcode 115. 不同的子序列 详解

本文探讨了在字符串S的子序列中计算字符串T出现次数的难题,通过递归和动态规划方法解决LeetCode第115题。文章详细解析了递归思路及其优化过程,最终采用动态规划实现高效解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

115. 不同的子序列
给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
示例 1:
输入: S = “rabbbit”, T = “rabbit”
输出: 3
解释:
如下图所示, 有 3 种可以从 S 中得到 “rabbit” 的方案。
(上箭头符号 ^ 表示选取的字母)
rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
示例 2:
输入: S = “babgbag”, T = “bag”
输出: 5
解释:
如下图所示, 有 5 种可以从 S 中得到 “bag” 的方案。
(上箭头符号 ^ 表示选取的字母)
babgbag
^^ ^
babgbag
^^ ^
babgbag
^ ^^
babgbag
^ ^^
babgbag
^^^
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/distinct-subsequences
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

挺难的一题。
刚拿到手,没头绪,仔细去想,先用递归吧。这种求方法数和最优解等等的要么用递归要么用dp。
修修改改的递归写法

class Solution {
    public int numDistinct(String s, String t) {
        int n = s.length();
        int m = t.length();
        return dfs(s, t, n - 1, m - 1);
    	}
    	public int dfs(String s, String t, int index1, int index2) {
    		if(index2 <= -1) return 1; //当t全部匹配完,可以算一种方法
    		if(index1 <= -1) return 0; //注意两个if的顺序正确
    		
    		if(s.charAt(index1) == t.charAt(index2)) {
    			return dfs(s, t, index1 - 1, index2 - 1) + dfs(s, t, index1 - 1, index2);
    		}
    		else return dfs(s, t, index1 - 1, index2);
    	}
    }

对是对了,就是超时
这个写法思路是啥呢,从最后开始匹配, index1和index2作为下标,如果对应的字符相等,我们可以继续往前匹配,也可以将s的下标前移一个,而index2不动,这是两种选择,而如果index2移到了0之前,那说明肯定有一种方法了,这个时候返回1,注意两个if的顺序,如果反一下就不对了。
但如果不等的话,那可以要将s的下标往前移去找和t里面相匹配的字符了。
只有一种选择。
这个写法我也琢磨了挺久的,还是菜啊。
然后意料之中的超时了,可以优化一下,存中间状态。

class Solution {
    public int numDistinct(String s, String t) {
        int n = s.length();
        int m = t.length();
        return dfs(s, t, n - 1, m - 1);
    }
    public int dfs(String s, String t, int index1, int index2) {
        if(index2 <= -1) return 1;
        if(index1 <= -1) return 0;
        int ans1 = dfs(s, t, index1 - 1, index2);
        if(s.charAt(index1) == t.charAt(index2)) {
            int ans2 = dfs(s, t, index1 - 1, index2 - 1);
            return ans1 + ans2;
        }
        else return ans1;
    }
}

结果很不给面子的两种都超时了,没办法那就用dp的方法吧…
在这里插入图片描述

有了递归的思路,写dp就要容易多了,改几行,初始化一下,其实dp就基本写好了。

class Solution {
    public int numDistinct(String s, String t) {
        int n = s.length();
        int m = t.length();
        if(m > n) return 0;
        int[][] dp = new int[m + 1][n + 1];
        for(int i = 0; i <= n; i ++) {
            dp[0][i] = 1;
        }
        for(int i = 1; i <= m; i ++) {
            for(int j = 1; j <= n; j ++) {
                if(t.charAt(i - 1) == s.charAt(j - 1)) {
                    dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1];
                }
                else dp[i][j] = dp[i][j - 1];
            }
        }
        return dp[m][n];
    }
}

除了边界情况有点坑…其他还好,效率一般般,50+,毕竟是二维的,也可以用dp[n][m]的形式来写…然后可以优化为一维数组,效率要提高不少。
这里我就不写了。
我觉得这道题还是挺难的。大家可以尝试先从递归写然后逐步优化。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值