LeetCode每日一题系列
题目:792. 匹配子序列的单词数
难度:普通
题目
给定字符串 s 和字符串数组 words, 返回 words[i] 中是s的子序列的单词个数 。
字符串的 子序列 是从原始字符串中生成的新字符串,可以从中删去一些字符(可以是none),而不改变其余字符的相对顺序。
例如, “ace” 是 “abcde” 的子序列。
示例
示例 1:
输入: s = “abcde”, words = [“a”,“bb”,“acd”,“ace”]
输出: 3
解释: 有三个是 s的子序列的单词: “a”, “acd”, “ace”。
示例 2:
输入: s = “dsahjpjauf”, words = [“ahjpjau”,“ja”,“ahbwzgqnuk”,“tnmlanowax”]
输出: 2
提示:
1 <= s.length <= 5 * 104
1 <= words.length <= 5000
1 <= words[i].length <= 50
words[i]和 s 都只由小写字母组成。
思路
哈希表+二分查找:
传统的双指针判断是否为子序列的方法在本题会超时,思考一下,这里对字符不匹配的情况做了大量重复判断,所以采用哈希表记录下来
s中出现的字符的所有下标,在遍历words中的某个字符串word时,当我们遍历到word[i]的下一个字符word[i+1]时,我们不需要再依次从s往后匹配,我们只需要找到哈希表中键为word[i+1],值为大于i的最小值的坐标即为所求坐标(这一步骤可以使用二分查找)。
题解
class Solution:
def numMatchingSubseq(self, s: str, words: List[str]) -> int:
# 哈希表,存放s中字符与下标的映射
dmap = {}
for i, c in enumerate(s):
if dmap.get(c):
dmap[c].append(i)
else:
dmap[c] = [i]
ans = 0
# 遍历words
for w in words:
# 判断w中是否所有字符都符合规则
ok = True
# 判断的初始最小下标为-1
idx = -1
# 遍历words[i]
for i in range(len(w)):
idxs = dmap.get(w[i], [])
# 二分查找
l, r = 0, len(idxs) - 1
while l < r :
mid = l + r >> 1
if dmap.get(w[i], [])[mid] > idx:
r = mid
else:
l = mid + 1
# 未找到符合要求的下标
if r < 0 or dmap.get(w[i], [])[r] <= idx:
# 判断条件为False,直接退出
ok = False
break
# 找到符合要求的下标判断下标需要更改
else:
idx = dmap.get(w[i], [])[r]
# 若全部字符符合要求则+1,否则不变
ans += 1 if ok else 0
return ans