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 <= 1000 <= 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作为一种新兴的群体智能优化算法,模仿了水母在海洋中觅食和迁移的行为,以求解非线性、复杂的优化问题。实验结果…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
