Leetcode 115. 不同的子序列

LeetCode 115. 不同的子序列 题目链接 LeetCode 115. 不同的子序列 题目描述 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是) 题目数据保证答案符合 32 位带符号整数范围。 示例 1: 输入:S = “rabbbit”, T = “rabbit” 输出:3 解释: 如下图所示, 有 3 种可以从 S 中得到 “ra 阅读详情

题目

给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。

一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)

示例 1:

输入: S = “rabbbit”, T = “rabbit”
输出: 3
解释:
如下图所示, 有 3 种可以从 S 中得到 “rabbit” 的方案。
(上箭头符号 ^ 表示选取的字母)
在这里插入图片描述

示例 2:

输入: S = “babgbag”, T = “bag”
输出: 5
解释:
如下图所示, 有 5 种可以从 S 中得到 “bag” 的方案。
(上箭头符号 ^ 表示选取的字母)
在这里插入图片描述

解答

解法一:回溯法

不计较效率的话,其实可以使用回溯法解决。

虽然会 TLE ,但是使用回溯法也是有其优势的!

它可以获得结果集里每一个子序列是由哪些下标组成的,而不仅仅只得到数量。

如果换个题目要求获得所有(子序列的下标组成),那么使用回溯法就再合适不过了。

代码
class Solution {
    
    private int n = 0;
    public int numDistinct(String s, String t) {
        backtrace(s.toCharArray(), 0, t.toCharArray(), 0);
        return n;
    }
    
    private void backtrace(char[] w1, int index1, char[] w2, int index2) {
        if(index2 == w2.length) {
            n ++;
            return;
        } else {
            for(int j = index1; j < w1.length - (w2.length - index2) + 1; j ++) {
                if(w1[j] != w2[index2]) continue;
                backtrace(w1, j + 1, w2, index2 + 1);
            }            
        }
    }
}
结果

在这里插入图片描述

解法二:动态规划

仅仅获得数量的话,可以使用动态规划的方式,因为本题具有最优子结构。

动态规划四要素:

  1. 状态定义:dp[i][j] 表示 t 字符串的前 i 个字符与 s 字符串的前 j 个字符能产生的子序列数量。
  2. 状态转移:
    1. 如果 t[i] == s[j] 有两种情况:
      1. 使用 s 的第 j 位置字符:dp[i - 1][j - 1]
      2. 不使用 s 的第 j 位置字符:dp[i][j - 1]
    2. 否则 t[i] != s[j]:其实相当于不使用 s 的第 j 位置字符,dp[i][j - 1]
  3. 初始化:dp[0][j] = 1
  4. 结果:dp[t.length()][s.length()]

一个示例的 dp 数组的二维值图:
在这里插入图片描述
复杂度:O(nm) 时间, O(nm) 空间 。

常规动规代码
class Solution {
    
    public int numDistinct(String s, String t) {
        int[][] dp = new int[t.length() + 1][s.length() + 1];
        Arrays.fill(dp[0], 1);
        
        for(int i = 1; i <= t.length(); i ++) {
            for(int j = 1; j <= s.length(); j ++) {
                if(s.charAt(j - 1) == t.charAt(i - 1)) {
                    dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1];
                } else {
                    dp[i][j] = dp[i][j - 1];
                }
            }
        }
        
        return dp[t.length()][s.length()];
    }
}
结果

在这里插入图片描述

优化代码一:行主序

如果将二维数组 dp 中 s 串的一行记录作为行值,O(nm) 空间可以优化成一维数组的 O(n) 空间。

但每一次动态规划都需要保存前一个位置的原值,避免对当前位置的值产生影响。

class Solution {
    
    public int numDistinct(String s, String t) {
        int[] dp = new int[s.length() + 1];
        Arrays.fill(dp, 1);
        
        int pre = 1;
        for(int i = 0; i < t.length(); i ++) {
            for(int j = 0; j <= s.length(); j ++) {
                int old = dp[j];
                if(j == 0) {
                    dp[j] = 0;
                } else if(t.charAt(i) == s.charAt(j - 1)) {
                    dp[j] = dp[j - 1] + pre;
                } else {
                    dp[j] = dp[j - 1];
                }
                
                pre = old;
            }
        }
        
        return dp[s.length()];
    }
}
结果

在这里插入图片描述

优化代码二:列主序 + 倒序

换一种方向:对于每个 s 字符串内的字符,都进行更新一次列的值。

仔细观察原来二维 dp 数组的值

  1. dp[i - 1][j - 1] 等价于列主序的 dp[j - 1]
  2. dp[i][j - 1] 等价于列主序的 dp[j] 原值

所以在列主序的状态转移方程就变成:

  1. s[i] == t[j] 时 :dp[j] = dp[j] + dp[j - 1]
  2. s[i] != t[j] 时 :dp[j] = dp[j]

因而使用列主序的方式可以简化很多代码。

然而此时如果按正序遍历的话当前修改仍然会影响后一个位置,可以使用倒序遍历解决。

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

在这里插入图片描述

最终的优化代码:列主序 + 倒序 + 哈希

从上述优化二可以看出,如果 s 字符串的第 i 个字符与 t 字符串的第 j 个字符不相等,那么原来的值就不会改变。

既然这样,能不能只找 t 字符串中与 s 字符串的第 i 个字符相同的位置呢?

答:可以使用 hash 数组解决。

主要变量解释如下:

  1. map[c] :表明此字符在 t 字符串当前索引位置 i 之前最后一次出现的索引位置。

  2. next[i] :表明与 t 字符串索引位置 i 的字符相同且在此位置之前最后一次出现的位置。

class Solution {
    
    public int numDistinct(String s, String t) {
        int[] dp = new int[t.length() + 1];
        dp[0] = 1;
        
        int[] map = new int[128];
        Arrays.fill(map, -1);

        int[] nexts = new int[t.length()];
        for(int i = 0 ; i < t.length(); i ++){
            int c = t.charAt(i);
            nexts[i] = map[c];
            map[c] = i;
        }

        for (int i = 0; i < s.length(); i ++){
            char c = s.charAt(i);
            for(int j = map[c]; j >= 0; j = nexts[j]){
                dp[j + 1] += dp[j];
            }
        }
        
        return dp[t.length()];
    }
}
结果

在这里插入图片描述

【困难】力扣算法题解析LeetCode115:不同的子序列 本文介绍了如何统计字符串 t 在字符串 s 的子序列中出现的次数。通过动态规划方法,定义一维数组 dp 存储匹配状态,其中 dp[j] 表示 t 的前 j 个字符在 s 的子序列中的出现次数。初始化 dp[0] = 1,遍历 s 的字符并反向更新 dp 数组,当字符匹配时累加匹配数。最终 dp[t.length()] 即为结果。该方法时间复杂度为 O(n*m),空间复杂度为 O(m)。附 Java 代码实现及测试用例演示。 阅读详情

相关推荐

Python量化投资入门教程:从零构建你的第一个交易策略

量化投资教程以数学模型和计算机程序实现超额收益,选用Python工具链完成数据获取、策略构建及回测。通过移动平均线交叉策略示例,展示数据预处理、信号生成、收益计算及可视化全流程,结合收益率、最大回撤和夏普比率评估策略,并建议参数优化与策略融合提升稳健性。

ylmx5201314的博客 2454

字符串中找子序列个数(dp)

第二位为已经查找到子序列字符的数量,一开始没想到。 e.g 给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。 字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE"是"ABCDE"的一个子序列,而"AEC"不是) 题目数据保证答案符合 32 位带符号整数范围。 输入:s = "rabbbit", t = "rabbit" 输出:3 解释: 如下图所示,有 3 种可以从 s 中得到 "rab...

t2Kxx的博客 2095

告别VBA!用Python+PyWin32自动化你的SolidWorks建模流程(附完整代码)

本文详细介绍了如何利用Python和PyWin32实现SolidWorks建模流程的自动化,替代传统的VBA宏开发。通过环境配置、宏代码转换、高级开发模式及工程化实践等实战内容,帮助机械设计师构建高效、可维护的自动化工作流,显著提升设计效率。

weixin_30485379的博客 364

2021-06-22-求字符串的子序列数

求一个字符串的子序列数,输入一个字符串s和子序列数的长度n,输出子序列数, 输入: word,2 输出: 6 解释: word的长度为2的子串有:wo,wr,wd,or,wd,rd 解题思路: 1、考虑使用组合公式C(s.size(),n),但是这样不能排除字符串中相邻的字符是相同的情况,比如s=“aaa”,n=2. 组合公式得到的结果是C(3.2)=3,但是实际答案只能是一种“aa”,所以直接组合公式是不正确的。 2、使用深度优先搜索,对于重复的子序列用set去重 #include <bits/s

ITAK_fans的博客 899

硬件基础(4):(7)ADC电压采集:软件校准(标定、拟合)

在实际项目中,**软件端校准**可以有效弥补硬件方面(分压电阻误差、放大器失调、参考源误差等)带来的系统性偏差,从而提高 ADC 测量精度。下面是一个常见的**多点实测校准**流程与思路,个人感觉传感器标定的原理也只这样。

FCZlll的博客 6240

画个板子玩一玩STM32F030F4P6,也许是最便宜的32bit MCU

前言,什么是STM32F030F4P6 STM32目前最便宜的芯片之一应该就是这一块,STM32F030F4P6,这一个芯片没有USB,但是其他都有,引脚少,价格低廉,如果找不到原厂,可以买替代的国产芯片,性能也一样。 不管怎么样也是Cortex-M0,比8bit和16bit的性能都强,总的来说就是低价,引脚少,体积小的同时,还容易开发,有很多模拟输入可玩,还有SPI,硬件I2C等等 所以,这一款MCU真的是从理论来说又便宜又好用,非常适合把玩。 画个板,最小系统 STM32F030其..

arenascat的博客 2万+

字符串子序列问题:

这里,字符串的子序列是字符串中一个或多个字符的串联,而不改变顺序。 一、就单纯的求解字符串有多少子序列,没有别的要求,思路:   我们可以这么认为,看作字符串的拿取问题,即从母串(长度为n)中拿出字符组成新的子序列,那么每一位的字符都有两种状态,拿与不拿,所以就是2n问题,但是你又不能全都不拿,所以就是2n-1;一般来说,就提前打表,该取余就取余; 二、不单纯的求解,即存在重复字符的处理问题:  ...

桃夭丶的博客 1054

开关电源辅助电源设计全流程拆解

地类型定义包含电路功率地(PGND)高压侧地,与主功率电路地相连整流桥负极、原边电感地、UC3842的功率地信号地(SGND)低压侧地,与控制/采样电路地相连副边整流管地、TL431地、MCU/运放地。

lyzh971103的博客 1468

leetcode_115. 不同的子序列

给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是) 示例 1: 输入: S = “rabbbit”, T = “rabbit” 输出: 3 解释: 如下图所示, 有 3 种可以从 S 中得到 ...

qq_37292201的博客 236

Linux新手必看:FinalShell连接不上?从检查IP到配置固定地址的完整排错指南

本文为Linux新手提供了FinalShell连接失败的完整排错指南,涵盖IP检查、SSH服务状态、防火墙配置和静态IP设置等关键步骤。通过详细的命令行操作和配置示例,帮助用户快速解决远程连接问题,提升Linux使用效率。

weixin_30675247的博客 753

RH850从0搭建Autosar开发环境【7】- Davinci Configurator之CanTp模块配置详解(上)

RH850从0搭建Autosar开发环境【7】- Davinci Configurator之CanTp模块配置详解(上)

weixin_42412049的博客 2297

( 动态规划) 115. 不同的子序列 ——【Leetcode每日一题】

115. 不同的子序列 给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数。 题目数据保证答案符合 32 位带符号整数范围。

weixin_43412762的博客 1024

黑猫带你学eMMC协议第9篇:eMMC初始化过程(initial)

本文依据eMMC JEDEC5.1及个人工作经验整理而成,如有错误请留言。 文章为个人辛苦整理,付费内容,禁止私自转载。 文章所在专栏:《黑猫带你学:eMMC协议详解》 1 什么是emmc的初始化 对于小白来说,可能不太明白什么是emmc初始化。举个栗子:当你拿到一个电脑,接上电源,是不是就立马可以用了呢?并不是吧,你还需要去开机,等待电脑启动,配置完各种参数,等出现了电脑桌面,你才能用。那么同样,对于emmc芯片来说,也不是提供电源就能用了,需要输入各种命令后,让其进入到tran state后,才能正.

黑猫奥利奥的博客 6738

力扣LeetCode #115 不同的子序列(numDistinct)

- 题目描述 给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是。 提示: 0 <= s.length, t.length <= 1000 s 和 t 由英文字母组成 题目数据保证答案符合 32 位带符号整数范围。 来源:LeetCode - 示例 示例 1: 输入:s = “rabbbi

weixin_49155948的博客 473

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

力扣第115题:不同的子序列 — C语言解法

我们在代码中定义了常量,用于防止结果的溢出。每次更新dp数组时,我们都对当前值取模,确保它不会超出long long类型的最大值。1000000007# define MOD 1000000007 // 定义常用的模数。

weixin_48941116的博客 1004

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【力扣】Go语言实现力扣115不同的子序列

详细解读如何使用Go语言实现力扣115不同的子序列,通过动态规划的方式解决子序列匹配问题,含代码实现与详细讲解。

tatasix的博客 1170

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

LeetCode 力扣 115. 不同的子序列

题目描述(困难难度) 给定两个字符串 S 和T,从 S 中选择字母,使得刚好和 T 相等,有多少种选法。 解法一 递归之分治 S 中的每个字母就是两种可能选他或者不选他。我们用递归的常规思路,将大问题化成小问题,也就是分治的思想。 如果我们求 S[0,S_len - 1] 中能选出多少个 T[0,T_len - 1],个数记为 n。那么分两种情况, S[0] == T[0],需要知道两种情况 ...

wind_liang的博客 1056

LeetCode 115:不同的子序列

设dp[i][j]s的前i个字符s[0..i-1]t的前j个字符t[0..j-1]的匹配方案数(即s的前i个字符中,子序列等于t的前j个字符的个数)。

m0_75126572的博客 1183

LeetCode第115题_不同的子序列

给定一个字符串s和一个字符串t,计算在s的子序列中t出现的个数。字符串的一个是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE"是"ABCDE"的一个子序列,而"AEC"不是)题目数据保证答案符合 32 位带符号整数范围。

qq_40263592的博客 1332

LeetCode 115.不同的子序列

LeetCode 115.不同的子序列 双序列型动态规划 确定状态: 类似于LCS的思路,以最后一步的T为出发点。 情况1:T[n-1]=A[m-1],则求T[0..n-2]在S[0..m-2]中出现多少次 情况2:T[n-1]!=S[m-1],则不考虑S[m-1],求T[0..n-1]在S[0..m-2]中出现多少次 -状态:设f[i][j]为T前j个字符T[0..j-1]在S前i个字符S[0...

未命名 441

【LeetCode】115. 不同的子序列

本文介绍了求解字符串s中子序列t出现次数的动态规划方法。通过构建二维DP数组dp[i][j]表示s前i个字符中t前j个字符出现的次数,当s[i-1]=t[j-1]时可以选择匹配(dp[i-1][j-1])或不匹配(dp[i-1][j]),否则只能不匹配(dp[i-1][j])。边界条件为dp[i][0]=1(空字符串子序列)和dp[0][j]=0(j>0)。给出了二维DP、滚动数组优化和递归+记忆化三种解法,时间复杂度均为O(n*m)。最优解法使用二维DP,而滚动数组优化可将空间复杂度降至O(m)。典

qq_32019341的博客 1228

【leetcode】115.不同的子序列

【leetcode】115.不同的子序列题目思路代码复杂度 题目 leetcode原题链接 给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。 字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是) 题目数据保证答案符合 32 位带符号整数范围。 示例 1: 输入:s = “rabbbit”, t = “rabbit” 输出:3 解释: 如下图所示

laplacepoisson的博客 489

Leetcode115 不同的子序列 C++,Java,Pytho

Leetcode115 不同的子序列 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/distinct-subsequences/ 博主Github:https://github.com/GDUT-Rp/LeetCode 题目: 给定一个字符串 S\mathbf{S}S 和一个字符串 T\mathbf{T}T,计算在 S\mathbf{S}...

Rp的博客 456

Leetcode 115. 不同的子序列 C++

Leetcode 115. 不同的子序列 题目 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是) 题目数据保证答案符合 32 位带符号整数范围。 测试样例 示例 1: 输入:S = "rabbbit", T = "rabbit" 输出:3 解释: 如下图所示, 有 3 种可以从 S 中得到 "rab

weixin_43750513的博客 253

leetcode-115:不同的子序列

leetcode-115:不同的子序列题目解题方法一:动态规划 题目 题目链接 给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。 字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是) 题目数据保证答案符合 32 位带符号整数范围。 示例 1: 输入:s = "rabbbit", t = "rabbit" 输出:3 解释: 如下图所示, 有 3 种可以从

菊头蝙蝠的博客 4662

力扣115. 不同的子序列(字符串+动态规划)

力扣115. 不同的子序列(字符串+动态规划) https://leetcode-cn.com/problems/distinct-subsequences/ 字符串+动态规划 从左往右匹配 class Solutionfront { public: int numDistinct(string s, string t) { int m = s.size(); int n = t.size(); if (m < n) { return 0; } v

小麦China的博客 755
上一篇: Leetcode 72. 编辑距离
下一篇: Leetcode 125. 验证回文串
一纸浮萍
博客等级 码龄9年 11粉丝 103原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值