一天一大 leet(判断子序列)难度:简单-Day20200727
时间:2022-07-25
本文章向大家介绍一天一大 leet(判断子序列)难度:简单-Day20200727,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
题目:
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例:
- 示例 1
s = "abc", t = "ahbgdc"
返回 true.
- 示例 2
s = "axc", t = "ahbgdc"
返回 false.
后续挑战 : 如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10 亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
抛砖引玉
思路
- 遍历 s,按索引取出 s 中的单个字符
- 在 t 中查询这个字符的位置,然后删除这个字符及其之前的字符
- 如果删除后 s 未遍历的字符比 t 上则不满足
- 如果变量完成都匹配则返回 true
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isSubsequence = function (s, t) {
let slen = s.length,
tlen = t.length
if (slen > tlen) return false
for (let i = 0; i < slen; i++) {
let index = t.indexOf(s[i])
if (index === -1) return false
t = t.substring(index + 1)
if (slen - i - 1 > t.length) return false
}
return true
}
- 上面每取出一个字符都需要在 t 中 indexof 查询,
- indexOf 的边界是通过 substring 截取字符串完成,
换种思路不具体操作字符串 s,而是通过索引来限制字符串查找范围
- index 默认从 0 开始
- s[i],不等于 t[index],则接着查询 index+1 位置,其中 index 小于 t.length,i>=index
- s[i]无论匹配结果如果,i 向后移动式查找范围缩小 index+1
- 如果 index === t.length 则说明 s===t,
- 如果 index>t.length 则 index 再查询时有字符未匹配到超出限制范围
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isSubsequence = function (s, t) {
let index = 0
for (let i = 0; i < s.length; i++) {
while (index < t.length && s[i] !== t[index]) {
index++
}
index++
}
return index <= t.length
}
双指针
- 上面是通过一个索引限制 t 的查询范围
- 更直观些,可以声明两个变量 s->i,t->j,分别表示两个字符串指针
- 匹配成功 i 递增,匹配下一个字符
- 当前位未匹配 j 递增,继续尝试匹配
- 边界:
- i 小于 s.length
- j 小于 t.length
- 触发边界条件终止时,t 变量完则说明 t 中字符全匹配了,不然返回 false
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isSubsequence = function (s, t) {
let slen = s.length,
tlen = t.length,
i = 0,
j = 0
while (i < slen && j < tlen) {
if (s.charAt(i) == t.charAt(j)) {
i++
}
j++
}
return i == slen
}
动态规划
- 设 t 长 tlen
- 声明一个 tlen*26 的矩阵 dp
- 矩阵中记录 t 中每个字符第一次出现所在的坐标点
- |
a |
h |
b |
g |
d |
c |
- |
---|---|---|---|---|---|---|---|
0 |
0 |
6 |
... |
... |
... |
... |
6 |
1 |
2 |
2 |
2 |
6 |
... |
... |
6 |
3 |
4 |
4 |
4 |
4 |
4 |
... |
6 |
4 |
5 |
5 |
5 |
5 |
5 |
5 |
6 |
5 |
... |
... |
... |
... |
... |
... |
6 |
6 |
3 |
3 |
3 |
3 |
6 |
... |
6 |
7 |
1 |
1 |
6 |
... |
6 |
||
... |
... |
... |
... |
... |
... |
... |
6 |
26 |
... |
... |
... |
... |
... |
... |
6 |
- 在生成 s 的矩阵时,因为无法预期第一次出现 t[i]的位置,则倒序查询默认填充 tlen(表示不存在):
- dp[i][j],在 a-z 中,等于的字符,则将 t 中索引存放到 dp[i][j]中
- dp[i][j],不等于的字符,则该位置不是 t[i]出现位置,其值沿用本行已计算的值 dp[i+1][j]
- 遍历 s,每一个字符对应 dp 一行
- 如果该行存放的位置为边界则说明匹配,t 中未查询到该字符
- 位置未越界则,继续查询后一个 s 中的字符,s 的其实位置+1
var isSubsequence = function (s, t) {
let slen = s.length,
tlen = t.length,
dp = Array.from({ length: tlen + 1 }, () => Array(26))
// 填充边界值
for (let i = 0; i < 26; i++) {
dp[tlen][i] = tlen
}
for (let i = tlen - 1; i >= 0; i--) {
// 生成dp记录每个字符第一次出现位置
for (let j = 0; j < 26; j++) {
if (t.charAt(i) === String.fromCharCode(j + 97)) {
dp[i][j] = i
} else {
dp[i][j] = dp[i + 1][j]
}
}
}
// 查询字符s[i],的t字符起始位置
let index = 0
for (let i = 0; i < slen; i++) {
// 遇到边界说明未匹配到
if (dp[index][s.charAt(i).charCodeAt() - 97] === tlen) {
return false
}
// 满足条件更新t起始位置
index = dp[index][s.charAt(i).charCodeAt() - 97] + 1
}
return true
}
正则
- s = "abc"
- t = "ahbgdc"
- 转换成正则表达式:a[a-Z]*b[a-Z]*c[a-Z]*
- 用时去匹配 t,查询 t 中是否包含满足该顺序字符
var isSubsequence = function (s, t) {
return new RegExp(s.split('').join('[a-z]*')).test(t)
}
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- deepin linux 手动升级内核的方法
- UGL之单色位图
- Linux网络启动问题:Device does not seem to be present解决办法
- UGL之透明位图
- 关于ISR
- python 井字棋-文字版(下)
- Linux下nginx生成日志自动切割的实现方法
- Centos 7.2中双网卡绑定及相关问题踩坑记录
- Linux双网卡绑定实现负载均衡详解
- 单台服务器中利用Apache的VirtualHost如何搭建多个Web站点详解
- linux系统下MongoDB单节点安装教程
- Centos 7系统虚拟机桥接模式详解
- Centos 6中编译配置httpd2.4的多种方法详解
- linux的最大打开文件数限制修改方法
- Shell中如何删除文本比较长的行的实现方法