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作为一种新兴的群体智能优化算法,模仿了水母在海洋中觅食和迁移的行为,以求解非线性、复杂的优化问题。实验结果…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...