LeetCode题练习与总结:判断子序列--392
一、题目描述
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"
是"abcde"
的一个子序列,而"aec"
不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
示例 1:
输入:s = "abc", t = "ahbgdc" 输出:true
示例 2:
输入:s = "axc", t = "ahbgdc" 输出:false
提示:
0 <= s.length <= 100
0 <= t.length <= 10^4
- 两个字符串都只由小写字符组成。
二、解题思路
这个问题可以通过双指针的方法来解决。我们定义两个指针,一个指向字符串s
,另一个指向字符串t
。我们遍历字符串t
,每当我们遇到一个与s
中当前字符相同的字符时,我们就移动s
的指针。如果s
的指针能够移动到s
的末尾,那么s
就是t
的子序列。
(一) 基本实现
- 初始化两个指针
i
和j
,分别指向s
和t
的起始位置。 - 遍历字符串
t
,如果t[j] == s[i]
,则i++
。 - 如果
i
等于s
的长度,返回true
。 - 如果遍历完
t
后,i
不等于s
的长度,返回false
。
(二) 进阶问题
对于进阶问题,如果需要检查大量的s
字符串是否为t
的子序列,我们可以预处理t
来创建一个映射,记录t
中每个字符出现的位置。这样,对于每个s
,我们可以快速地检查它是否为t
的子序列,而不需要每次都遍历t
。
三、具体代码
以下是基本实现的Java代码:
class Solution {public boolean isSubsequence(String s, String t) {int i = 0, j = 0;while (i < s.length() && j < t.length()) {if (s.charAt(i) == t.charAt(j)) {i++;}j++;}return i == s.length();}
}
以下是进阶问题的预处理和检查方法的Java代码:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;class Solution {Map<Character, List<Integer>> indexMap;public boolean isSubsequence(String s, String t) {// 预处理tpreprocess(t);// 检查s是否为t的子序列return checkSubsequence(s);}private void preprocess(String t) {indexMap = new HashMap<>();for (int i = 0; i < t.length(); i++) {char c = t.charAt(i);indexMap.computeIfAbsent(c, x -> new ArrayList<>()).add(i);}}private boolean checkSubsequence(String s) {int prevIndex = -1;for (char c : s.toCharArray()) {if (!indexMap.containsKey(c)) {return false;}List<Integer> indices = indexMap.get(c);int pos = binarySearch(indices, prevIndex);if (pos == -1) {return false;}prevIndex = indices.get(pos) + 1;}return true;}private int binarySearch(List<Integer> indices, int target) {int left = 0, right = indices.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;if (indices.get(mid) > target) {right = mid - 1;} else {left = mid + 1;}}return left < indices.size() && indices.get(left) > target ? left : -1;}
}
这里的preprocess
方法预处理了字符串t
,checkSubsequence
方法用于检查字符串s
是否为t
的子序列。binarySearch
方法用于在预处理后的列表中找到第一个大于target
的索引。
四、时间复杂度和空间复杂度
(一) 基本实现
1. 时间复杂度
代码的时间复杂度主要取决于字符串s
和t
的长度。
while
循环的条件是i < s.length()
和j < t.length()
,这意味着循环会持续直到至少一个字符串被完全遍历。- 在循环内部,我们执行了常数时间的操作(比较字符和增加指针)。
因此,循环将执行O(s.length() + t.length())
次。这是因为每次循环中,我们至少将j
增加1,直到t
被完全遍历,而i
的增加则最多与s
的长度相同。所以,时间复杂度是O(n + m)
,其中n
是字符串t
的长度,m
是字符串s
的长度。
2. 空间复杂度
代码的空间复杂度主要取决于除了输入字符串之外所使用的额外空间。
i
和j
是两个整型变量,它们占用的空间是常数,即O(1)
。- 没有使用任何其他的数据结构,如数组、列表或哈希表。
因此,空间复杂度是O(1)
,因为无论输入字符串s
和t
的长度如何,使用的额外空间都不会改变。
(二) 进阶问题
1. 时间复杂度
代码的时间复杂度可以分为预处理阶段和检查子序列阶段。
(1) 预处理阶段
preprocess
方法:- 遍历字符串
t
,其长度为n
。 - 对于每个字符
c
,将其索引添加到对应的列表中,这是一个常数时间的操作。
- 遍历字符串
- 因此,预处理的时间复杂度为 O(n)。
(2) 检查子序列阶段
checkSubsequence
方法:- 遍历字符串
s
,其长度为m
。 - 对于
s
中的每个字符,我们执行以下操作:- 检查字符是否在
indexMap
中,这是常数时间的操作。 - 使用
binarySearch
方法在对应的索引列表中查找第一个大于prevIndex
的位置。 binarySearch
方法的时间复杂度为 O(log k),其中k
是列表中元素的数量,对于每个字符c
,k
最多为n
。
- 检查字符是否在
- 遍历字符串
- 因此,检查子序列的总时间复杂度为 O(m log n)。
综合两个阶段,总的时间复杂度为 O(n + m log n)。
2. 空间复杂度
indexMap
:- 存储了字符串
t
中每个字符的所有索引位置。 - 假设字符集大小为
C
(对于小写字母,C
为 26),在最坏情况下,indexMap
可能包含n
个条目,每个条目对应一个字符的索引列表。 - 每个列表的平均长度为
n / C
,所以总的存储空间为 O(n)。
- 存储了字符串
binarySearch
方法:- 使用了常数额外空间,即 O(1)。
因此,总的空间复杂度为 O(n)。
五、总结知识点
(一) 基本实现
-
类定义:
class Solution
:定义了一个名为Solution
的类。
-
方法定义:
public boolean isSubsequence(String s, String t)
:定义了一个公共方法isSubsequence
,它接受两个字符串参数s
和t
,并返回一个布尔值。
-
变量声明与初始化:
int i = 0, j = 0;
:声明并初始化了两个整型变量i
和j
,用于在字符串s
和t
中遍历。
-
循环结构:
while (i < s.length() && j < t.length())
:使用while
循环来遍历字符串s
和t
,直到至少一个字符串被完全遍历。
-
字符串操作:
s.charAt(i)
:使用charAt
方法来获取字符串s
中索引为i
的字符。t.charAt(j)
:使用charAt
方法来获取字符串t
中索引为j
的字符。
-
条件判断:
if (s.charAt(i) == t.charAt(j))
:条件判断语句,用于检查字符串s
和t
在当前位置的字符是否相等。
-
变量自增:
i++
:当条件满足时,i
的值自增,表示在字符串s
中找到了一个匹配的字符。j++
:无论条件是否满足,j
的值都会自增,表示在字符串t
中移动到下一个字符。
-
方法返回值:
return i == s.length();
:返回一个布尔值,表示字符串s
是否完全在字符串t
中找到,即s
是否为t
的子序列。
-
逻辑运算符:
&&
:逻辑与运算符,用于在while
循环中组合两个条件。
-
比较运算符:
==
:用于比较两个值是否相等。
(二) 进阶问题
-
类定义:
class Solution
:定义了一个名为Solution
的类。
-
成员变量:
Map<Character, List<Integer>> indexMap
:定义了一个成员变量indexMap
,它是一个哈希表,键是字符,值是该字符在字符串中出现的索引列表。
-
构造方法:
- (无显式构造方法,但隐式有一个无参构造方法)。
-
方法定义:
public boolean isSubsequence(String s, String t)
:定义了一个公共方法isSubsequence
,它接受两个字符串参数并返回一个布尔值。
-
预处理方法:
private void preprocess(String t)
:定义了一个私有方法preprocess
,用于预处理字符串t
并填充indexMap
。
-
检查子序列方法:
private boolean checkSubsequence(String s)
:定义了一个私有方法checkSubsequence
,用于检查字符串s
是否为字符串t
的子序列。
-
二分查找方法:
private int binarySearch(List<Integer> indices, int target)
:定义了一个私有方法binarySearch
,用于在有序列表indices
中查找第一个大于target
的索引。
-
数据结构:
HashMap
和ArrayList
:使用了哈希表和动态数组来存储字符及其索引。
-
集合操作:
computeIfAbsent
:在HashMap
中,如果键不存在,则计算其值并插入到映射中。
-
循环结构:
for
循环:用于遍历字符串t
并填充indexMap
。while
循环:在binarySearch
方法中用于实现二分查找。
-
字符操作:
char c = t.charAt(i)
:获取字符串t
中第i
个位置的字符。
-
逻辑判断:
if
和else
语句:用于条件判断。
-
递增和递减操作:
i++
和j++
:用于在字符串中移动指针。left++
和right--
:在二分查找中调整搜索范围。
-
返回值:
return
语句:用于从方法中返回结果。
-
比较操作:
>
和>=
:用于比较整数。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。
相关文章:
LeetCode题练习与总结:判断子序列--392
一、题目描述 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一…...

json数据结构的转换
# json可用于赌徒与原件数据的转换(json以字符串的形式储存数据,在通过json进行两种语言的转换时,应先将数据类型转换成列表或字典,再由列表或字典转换成json字符串,最后由json字符串转换成另一种语言的列表或字典数据…...
mysql删除语句:@Update(“TRUNCATE TABLE employee“)讲解
这个 SQL 语句: TRUNCATE TABLE employee是一个 SQL DDL(数据定义语言) 操作,用于清空数据库表中的所有记录,但不会删除表结构(即列和索引等)。 逐部分解释: TRUNCATE:…...
如何修改浏览器指纹?
网络安全日益重要,我们的上网行为变得越来越容易被追踪和分析。其中,浏览器指纹就是一种强大的技术手段,它可以说是你上网的身份象征。 一、浏览器指纹是什么 浏览器指纹是网站和在线平台用来收集关于你的浏览器、设备和网络的详细信息的一…...

实现3D热力图
实现思路 首先是需要用canvas绘制一个2D的热力图,如果你还不会,请看json绘制热力图。使用Threejs中的canvas贴图,将贴图贴在PlaneGeometry平面上。使用着色器材质,更具json中的数据让平面模型 拔地而起。使用Threejs内置的TWEEN&…...
GEE ui界面实现:用户自画多边形, 按面积比例在多边形中自动生成样点,导出多边形和样点shp,以及删除上一组多边形和样点(有视频效果展示)
零、背景 这几天在选样点,发现GEE有强大的ui功能,于是应用在我的工作上。 下述代码实现了几个功能: ①用户可以自己勾勒多边形,随后程序会按面积比例在多边形中自动生成样点,同时根据改多边形的区域生成区域平均月N…...
React diff算法和Vue diff算法的主要区别
React和Vue都是流行的前端框架,它们各自实现了diff算法来优化虚拟DOM的更新过程。以下是React diff算法和Vue diff算法的主要区别: 1. diff策略 React diff算法: React的diff算法主要采用了同层级比较的策略,即它不会跨层级比较节…...

WSL 2 中 FastReport 与 FastCube 的设置方法与优化策略
软件开发人员长期以来一直在思考这个问题:“我们如何才能直接在 Windows 中运行 Linux 应用程序,而无需使用单独的虚拟机?” WSL 技术为这个问题提供了一个可能的答案。WSL 的历史始于 2016 年。当时,其实现涉及使用 Windows 内核…...
《线性代数》学习笔记
列向量无关 上个星期继续学线性代数,一个矩阵,如何判断它是的列向量有几个是线性无关呢?其实有好几个方法。第一个就是一个一个判断。 先选定一个,然后看下这两个,怎么看呢?如果两个列向量线性相关&#…...

Redis三种集群模式:主从模式、哨兵模式和Cluster模式
目录标题 1、背景及介绍2、 Redis 主从复制2.1、主从复制特点2.2、Redis主从复制原理2.3 PSYNC 工作原理2.3.1、启动或重连判断:2.3.2、第一次同步处理:2.3.3、断线重连处理:2.3.4、主节点响应2.3.5、全量同步触发条件:2.3.6、复制…...

CDH大数据平台部署
二、CDH简介 全称Cloudera’s Distribution Including Apache Hadoop。 hadoop的版本 (Apache、CDH、Hotonworks版本) 在公司中一般使用cdh多一些(收费的)、也有公司使用阿里云大数据平台、微软的大数据平台。 国内也有一些平台:星环大数…...

7.4、实验四:RIPv2 认证和触发式更新
源文件 一、引言:为什么要认证和采用触发式更新? 1. RIP v2 认证 RIP(Routing Information Protocol)版本 2 添加了认证功能,以提高网络的安全性。认证的作用主要包括以下几点: 防止路由欺骗 RIP v1 是不…...

【一步步开发AI运动小程序】二十一、如果将AI运动项目配置持久化到后端?
**说明:**本文所涉及的AI运动识别、计时、计数能力,都是基于云智「Ai运动识别引擎」实现。云智「Ai运动识别」插件识别引擎,可以为您的小程序或Uni APP赋于原生、本地、广覆盖、高性能的人体识别、姿态识别、10余种常见的运动计时、计数识别及…...

LED和QLED的区别
文章目录 1. 基础背光技术2. 量子点技术的引入3. 色彩表现4. 亮度和对比度5. 能效6. 寿命7. 价格总结 LED和 QLED都是基于液晶显示(LCD)技术的电视类型,但它们在显示技术、色彩表现和亮度方面有一些关键区别。以下是两者的详细区别ÿ…...

2024 年Postman 如何安装汉化中文版?
2024 年 Postman 的汉化中文版安装教程...
转化古老的Eclipse项目为使用gradle构建
很多古老的Java项目,是使用Eclipse作为IDE开发的。 那么,使用其它IDE的开发者,如何快速地进入这种古老项目的开发呢?或者说,一个Eclipse构建的古老项目,能不能转化成一个IDE无关的项目,进而所有…...
openGauss常见问题与故障处理(二)
2.网络故障定位手段 2.1 网络故障定位手段--常见网络故障引发的异常 在数据库正常工作的情况下,网络层对上层用户是透明的,但数据库在长期运行时,可能会由于各种原因导致出现网络异常或错误。 常见的因网络故障引发的异常有: 1>…...

Mysql 8迁移到达梦DM8遇到的报错
在实战迁移时,遇到两个报错。 一、列[tag]长度超出定义 在mysql中,tag字段的长度是varchar(20),在迁移到DM8后,这个长度不够用了。怎么解决? 在迁移过程中,“指定对象”时,选择转换。 在“列映…...
Android HandlerThread 基础
HandlerThread **一、HandlerThread的基本概念和用途**1. **目的**2. **与普通线程的区别** **二、HandlerThread的使用步骤**1. **创建HandlerThread对象并启动线程**2. **创建Handler并关联到HandlerThread的消息队列**3. **发送消息到HandlerThread的消息队列** **三、Handl…...

【智能算法应用】人工水母搜索算法求解二维路径规划问题
摘要 本文基于人工水母搜索算法(Jellyfish Search Algorithm, JSA),对二维路径规划问题进行了研究。JSA作为一种新兴的群体智能优化算法,模仿了水母在海洋中觅食和迁移的行为,以求解非线性、复杂的优化问题。实验结果…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...