代码训练(36)判断子序列
Author: Once Day Date: 2025年6月17日
漫漫长路,才刚刚开始…
全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客
参考文章:
- 125. 验证回文串 - 力扣(LeetCode)
- 力扣 (LeetCode) 全球极客挚爱的技术成长平台
文章目录
- 代码训练(36)判断子序列
- 1. 原题
- 2. 分析
- 3. 代码实现
- 4. 总结
1. 原题
给定字符串 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
2. 分析
本题要求判断一个字符串 s 是否是另一个字符串 t 的子序列。子序列的定义是在不改变字符顺序的情况下,可以通过删除一些字符(或不删除)从 t 中得到 s。
基本方法:双指针法: 使用两个指针,一个指向 s,一个指向 t。同时遍历 s 和 t,如果字符匹配,则两个指针都向前移动;如果不匹配,只移动指向 t 的指针。如果能在 t 的末尾之前或同时遍历完 s,则 s 是 t 的子序列。
进阶方法:针对大量输入的 S1, S2, …, Sk 检查是否为 T 的子序列,可以使用以下方法优化:
- 预处理 T:创建一个映射,记录 t 中每个字符出现的所有位置。
- 二分查找:对于每个输入的 s,使用二分查找快速定位每个字符在 t 中的位置,确保它们的顺序符合子序列的要求。
分析步骤:
- 初始化: 创建两个指针,分别指向 s 和 t 的起始位置。
- 遍历比较: 同时遍历 s 和 t,根据字符是否匹配移动指针。
- 检查结果: 查看 s 的指针是否已经遍历完整个字符串,如果是,则 s 为 t 的子序列。
举例分析,假设 s = “abc”, t = “ahbgdc”:
- 初始化两个指针分别指向 s 和 t 的起始位置。
- 比较 s[0] 和 t[0] => ‘a’ == ‘a’,两个指针都前进。
- 比较 s[1] 和 t[1] => ‘b’ != ‘h’,只移动指向 t 的指针。
- 比较 s[1] 和 t[2] => ‘b’ == ‘b’,两个指针都前进。
- 比较 s[2] 和 t[3] => ‘c’ != ‘g’,只移动指向 t 的指针。
- 比较 s[2] 和 t[4] => ‘c’ != ‘d’,只移动指向 t 的指针。
- 比较 s[2] 和 t[5] => ‘c’ == ‘c’,两个指针都前进。
- 所有字符匹配完成,s 是 t 的子序列。
性能优化关键点:
- 减少不必要的比较: 使用双指针法避免对整个字符串的全面扫描。
- 减少重复检查: 对于进阶问题,通过预处理和二分查找减少重复的位置检查。
3. 代码实现
#include <stdbool.h>
#include <string.h>bool isSubsequence(char *s, char *t) {int indexS = 0, indexT = 0;int lenS = strlen(s), lenT = strlen(t);while (indexS < lenS && indexT < lenT) {if (s[indexS] == t[indexT]) {indexS++;}indexT++;}return indexS == lenS;
}
4. 总结
本题考察了字符串操作和双指针技巧,对于基础的子序列检查问题,使用双指针法已足够高效。对于进阶问题,通过合理的数据结构和算法优化,如预处理和二分查找,可以有效应对大规模数据的挑战。通过这类问题,可以加强对字符串处理和算法优化的理解和应用,有助于提高编程能力和解决实际问题的能力。