From 06449a10e0b6c1e6f7d015b2a6b0ae703842af33 Mon Sep 17 00:00:00 2001 From: AC_Oier Date: Thu, 17 Nov 2022 10:18:46 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8feat:=20add=20792=20&=20775?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "Index/\344\272\214\345\210\206.md" | 1 + .../\345\223\210\345\270\214\350\241\250.md" | 1 + ...10\344\270\255\347\255\211\357\274\211.md" | 15 +- ...10\344\270\255\347\255\211\357\274\211.md" | 4 +- ...10\344\270\255\347\255\211\357\274\211.md" | 163 ++++++++++++++++++ 5 files changed, 175 insertions(+), 9 deletions(-) create mode 100644 "LeetCode/791-800/792. \345\214\271\351\205\215\345\255\220\345\272\217\345\210\227\347\232\204\345\215\225\350\257\215\346\225\260\357\274\210\344\270\255\347\255\211\357\274\211.md" diff --git "a/Index/\344\272\214\345\210\206.md" "b/Index/\344\272\214\345\210\206.md" index 876d7072..55ff58b2 100644 --- "a/Index/\344\272\214\345\210\206.md" +++ "b/Index/\344\272\214\345\210\206.md" @@ -43,6 +43,7 @@ | [744. 寻找比目标字母大的最小字母](https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/solution/by-ac_oier-to07/) | 简单 | 🤩🤩🤩🤩🤩 | | [778. 水位上升的泳池中游泳](https://leetcode-cn.com/problems/swim-in-rising-water/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/swim-in-rising-water/solution/gong-shui-san-xie-yi-ti-shuang-jie-krusk-7c6o/) | 困难 | 🤩🤩🤩 | | [786. 第 K 个最小的素数分数](https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/solution/gong-shui-san-xie-yi-ti-shuang-jie-you-x-8ymk/) | 中等 | 🤩🤩🤩 | +| [792. 匹配子序列的单词数](https://leetcode.cn/problems/number-of-matching-subsequences/) | [LeetCode 题解链接](https://leetcode.cn/problems/number-of-matching-subsequences/solution/by-ac_oier-u1ox/) | 中等 | 🤩🤩🤩🤩 | | [793. 阶乘函数后 K 个零](https://leetcode.cn/problems/preimage-size-of-factorial-zeroes-function/) | [LeetCode 题解链接](https://leetcode.cn/problems/preimage-size-of-factorial-zeroes-function/solution/by-ac_oier-pk9g/) | 困难 | 🤩🤩🤩🤩 | | [852. 山脉数组的峰顶索引](https://leetcode-cn.com/problems/peak-index-in-a-mountain-array/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/peak-index-in-a-mountain-array/solution/gong-shui-san-xie-er-fen-san-fen-cha-zhi-5gfv/) | 简单 | 🤩🤩🤩🤩🤩 | | [875. 爱吃香蕉的珂珂](https://leetcode.cn/problems/koko-eating-bananas/) | [LeetCode 题解链接](https://leetcode.cn/problems/koko-eating-bananas/solution/by-ac_oier-4z7i/) | 中等 | 🤩🤩🤩🤩 | diff --git "a/Index/\345\223\210\345\270\214\350\241\250.md" "b/Index/\345\223\210\345\270\214\350\241\250.md" index d6925f3c..47a0f4ce 100644 --- "a/Index/\345\223\210\345\270\214\350\241\250.md" +++ "b/Index/\345\223\210\345\270\214\350\241\250.md" @@ -47,6 +47,7 @@ | [726. 原子的数量](https://leetcode-cn.com/problems/number-of-atoms/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-atoms/solution/gong-shui-san-xie-shi-yong-xiao-ji-qiao-l5ak4/) | 困难 | 🤩🤩🤩🤩 | | [728. 自除数](https://leetcode-cn.com/problems/self-dividing-numbers/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/self-dividing-numbers/solution/by-ac_oier-pvb1/) | 简单 | 🤩🤩🤩 | | [736. Lisp 语法解析](https://leetcode.cn/problems/parse-lisp-expression/) | [LeetCode 题解链接](https://leetcode.cn/problems/parse-lisp-expression/solution/by-ac_oier-i7w1/) | 困难 | 🤩🤩🤩🤩 | +| [792. 匹配子序列的单词数](https://leetcode.cn/problems/number-of-matching-subsequences/) | [LeetCode 题解链接](https://leetcode.cn/problems/number-of-matching-subsequences/solution/by-ac_oier-u1ox/) | 中等 | 🤩🤩🤩🤩 | | [811. 子域名访问计数](https://leetcode.cn/problems/subdomain-visit-count/) | [LeetCode 题解链接](https://leetcode.cn/problems/subdomain-visit-count/solution/by-ac_oier-aex6/) | 中等 | 🤩🤩🤩🤩 | | [817. 链表组件](https://leetcode.cn/problems/linked-list-components/) | [LeetCode 题解链接](https://leetcode.cn/problems/linked-list-components/solution/by-ac_oier-3gl5/) | 中等 | 🤩🤩🤩🤩 | | [846. 一手顺子](https://leetcode-cn.com/problems/hand-of-straights/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/hand-of-straights/solution/gong-shui-san-xie-shu-ju-jie-gou-mo-ni-t-4hxw/) | 中等 | 🤩🤩🤩 | diff --git "a/LeetCode/161-170/162. \345\257\273\346\211\276\345\263\260\345\200\274\357\274\210\344\270\255\347\255\211\357\274\211.md" "b/LeetCode/161-170/162. \345\257\273\346\211\276\345\263\260\345\200\274\357\274\210\344\270\255\347\255\211\357\274\211.md" index b25737e2..28a4be41 100644 --- "a/LeetCode/161-170/162. \345\257\273\346\211\276\345\263\260\345\200\274\357\274\210\344\270\255\347\255\211\357\274\211.md" +++ "b/LeetCode/161-170/162. \345\257\273\346\211\276\345\263\260\345\200\274\357\274\210\344\270\255\347\255\211\357\274\211.md" @@ -4,13 +4,15 @@ Tag : 「二分」 + + 峰值元素是指其值严格大于左右相邻值的元素。 -给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。 +给你一个整数数组 `nums`,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。 -你可以假设 nums[-1] = nums[n] = -∞ 。 +你可以假设 `nums[-1] = nums[n] = -∞` 。 -你必须实现时间复杂度为 O(log n) 的算法来解决此问题。 +你必须实现时间复杂度为 $O(\log{n})$ 的算法来解决此问题。 示例 1: ``` @@ -30,11 +32,10 @@ Tag : 「二分」   或者返回索引 5, 其峰值元素为 6。 ``` - 提示: -* 1 <= nums.length <= 1000 -* -$2^{31}$ <= nums[i] <= $2^{31}$ - 1 -* 对于所有有效的 i 都有 nums[i] != nums[i + 1] +* $1 <= nums.length <= 1000$ +* $-2^{31} <= nums[i] <= 2^{31} - 1$ +* 对于所有有效的 `i` 都有 `nums[i] != nums[i + 1]` --- diff --git "a/LeetCode/771-780/775. \345\205\250\345\261\200\345\200\222\347\275\256\344\270\216\345\261\200\351\203\250\345\200\222\347\275\256\357\274\210\344\270\255\347\255\211\357\274\211.md" "b/LeetCode/771-780/775. \345\205\250\345\261\200\345\200\222\347\275\256\344\270\216\345\261\200\351\203\250\345\200\222\347\275\256\357\274\210\344\270\255\347\255\211\357\274\211.md" index 2f456e1d..7d2e8edc 100644 --- "a/LeetCode/771-780/775. \345\205\250\345\261\200\345\200\222\347\275\256\344\270\216\345\261\200\351\203\250\345\200\222\347\275\256\357\274\210\344\270\255\347\255\211\357\274\211.md" +++ "b/LeetCode/771-780/775. \345\205\250\345\261\200\345\200\222\347\275\256\344\270\216\345\261\200\351\203\250\345\200\222\347\275\256\357\274\210\344\270\255\347\255\211\357\274\211.md" @@ -8,7 +8,7 @@ Tag : 「树状数组」、「数学」 给你一个长度为 `n` 的整数数组 `nums`,表示由范围 $[0, n - 1]$ 内所有整数组成的一个排列。 -全局倒置 的数目等于满足下述条件不同下标对 (i, j) 的数目: +全局倒置 的数目等于满足下述条件不同下标对 $(i, j)$ 的数目: * $0 <= i < j < n$ * $nums[i] > nums[j]$ @@ -38,7 +38,7 @@ Tag : 「树状数组」、「数学」 ``` 提示: -* $n == nums.length$ +* $n = nums.length$ * $1 <= n <= 10^5$ * $0 <= nums[i] < n$ * `nums` 中的所有整数 互不相同 diff --git "a/LeetCode/791-800/792. \345\214\271\351\205\215\345\255\220\345\272\217\345\210\227\347\232\204\345\215\225\350\257\215\346\225\260\357\274\210\344\270\255\347\255\211\357\274\211.md" "b/LeetCode/791-800/792. \345\214\271\351\205\215\345\255\220\345\272\217\345\210\227\347\232\204\345\215\225\350\257\215\346\225\260\357\274\210\344\270\255\347\255\211\357\274\211.md" new file mode 100644 index 00000000..5595184a --- /dev/null +++ "b/LeetCode/791-800/792. \345\214\271\351\205\215\345\255\220\345\272\217\345\210\227\347\232\204\345\215\225\350\257\215\346\225\260\357\274\210\344\270\255\347\255\211\357\274\211.md" @@ -0,0 +1,163 @@ +### 题目描述 + +这是 LeetCode 上的 **[792. 匹配子序列的单词数](https://leetcode.cn/problems/number-of-matching-subsequences/solution/by-ac_oier-u1ox/)** ,难度为 **中等**。 + +Tag : 「二分」、「哈希表」 + + + +给定字符串 `s` 和字符串数组 `words`, 返回  `words[i]` 中是 `s` 的子序列的单词个数 。 + +字符串的 子序列 是从原始字符串中生成的新字符串,可以从中删去一些字符(可以是`""`),而不改变其余字符的相对顺序。 + +例如, `“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 \times 10^4$ +* $1 <= words.length <= 5000$ +* $1 <= words[i].length <= 50$ +* `words[i]` 和 `s` 都只由小写字母组成。 + +--- + +### 预处理 + 哈希表 + 二分 + +朴素判定某个字符串是为另一字符串的子序列的复杂度为 $O(n + m)$,对于本题共有 $5000$ 个字符串需要判定,每个字符串最多长为 $50$,因此整体计算量为 $(5 \times 10^4 + 50) \times 5000 \approx 2.5 \times 10^8$,会超时。 + +不可避免的是,我们要对每个 $words[i]$ 进行检查,因此优化的思路可放在如何优化单个 $words[i]$ 的判定操作。 + +朴素的判定过程需要使用双指针扫描两个字符串,其中对于原串的扫描,会有大量的字符会被跳过(无效匹配),即只有两指针对应的字符相同时,匹配串指针才会后移。 + +我们考虑如何优化这部分无效匹配。 + +对于任意一个 $w = words[i]$ 而言,假设我们当前匹配到 $w[j]$ 位置,此时我们已经明确下一个待匹配的字符为 $w[j + 1]$,因此我们可以直接在 `s` 中字符为 $w[j + 1]$ 的位置中找候选。 + +具体的,我们可以使用哈希表 `map` 对 `s` 进行预处理:以字符 $c = s[i]$ 为哈希表的 `key`,对应的下标 $i$ 集合为 `value`,由于我们从前往后处理 `s` 进行预处理,因此对于所有的 `value` 均满足递增性质。 + +> 举个 🌰 : 对于 `s = abcabc` 而言,预处理的哈希表为 `{a=[0,3], b=[1,4], c=[2,5]}` + +最后考虑如何判定某个 $w = words[i]$ 是否满足要求:待匹配字符串 `w` 长度为 `m`,我们从前往后对 `w` 进行判定,假设当前判待匹配位置为 $w[i]$,我们使用变量 `idx` 代表能够满足匹配 $w[0:i]$ 的最小下标(贪心思路)。 + +对于匹配的 $w[i]$ 字符,可以等价为在 `map[w[i]]` 中找到第一个大于 `idx` 的下标,含义在原串 `s` 中找到字符为 `w[i]` 且下标大于 `idx` 的最小值,由于我们所有的 `map[X]` 均满足单调递增,该过程可使用「二分」进行。 + +Java 代码: +```Java +class Solution { + public int numMatchingSubseq(String s, String[] words) { + int n = s.length(), ans = 0; + Map> map = new HashMap<>(); + for (int i = 0; i < n; i++) { + List list = map.getOrDefault(s.charAt(i), new ArrayList<>()); + list.add(i); + map.put(s.charAt(i), list); + } + for (String w : words) { + boolean ok = true; + int m = w.length(), idx = -1; + for (int i = 0; i < m && ok; i++) { + List list = map.getOrDefault(w.charAt(i), new ArrayList<>()); + int l = 0, r = list.size() - 1; + while (l < r) { + int mid = l + r >> 1; + if (list.get(mid) > idx) r = mid; + else l = mid + 1; + } + if (r < 0 || list.get(r) <= idx) ok = false; + else idx = list.get(r); + } + if (ok) ans++; + } + return ans; + } +} +``` +TypeScript 代码: +```TypeScript +function numMatchingSubseq(s: string, words: string[]): number { + let n = s.length, ans = 0 + const map = new Map>() + for (let i = 0; i < n; i++) { + if (!map.has(s[i])) map.set(s[i], new Array()) + map.get(s[i]).push(i) + } + for (const w of words) { + let ok = true + let m = w.length, idx = -1 + for (let i = 0; i < m && ok; i++) { + if (!map.has(w[i])) { + ok = false + } else { + const list = map.get(w[i]) + let l = 0, r = list.length - 1 + while (l < r) { + const mid = l + r >> 1 + if (list[mid] > idx) r = mid + else l = mid + 1 + } + if (r < 0 || list[r] <= idx) ok = false + else idx = list[r] + } + } + if (ok) ans++ + } + return ans +} +``` +Python3 代码: +```Python3 +class Solution: + def numMatchingSubseq(self, s: str, words: List[str]) -> int: + dmap = defaultdict(list) + for i, c in enumerate(s): + dmap[c].append(i) + ans = 0 + for w in words: + ok = True + idx = -1 + for i in range(len(w)): + idxs = dmap[w[i]] + l, r = 0, len(idxs) - 1 + while l < r : + mid = l + r >> 1 + if dmap[w[i]][mid] > idx: + r = mid + else: + l = mid + 1 + if r < 0 or dmap[w[i]][r] <= idx: + ok = False + break + else: + idx = dmap[w[i]][r] + ans += 1 if ok else 0 + return ans +``` +* 时间复杂度:令 `n` 为 `s` 长度,`m` 为 `words` 长度,`l = 50` 为 $words[i]$ 长度的最大值。构造 `map` 的复杂度为 $O(n)$;统计符合要求的 $words[i]$ 的数量复杂度为 $O(m \times l \times \log{n})$。整体复杂度为 $O(n + m \times l \times \log{n})$ +* 空间复杂度:$O(n)$ + +--- + +### 最后 + +这是我们「刷穿 LeetCode」系列文章的第 `No.792` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 + +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 + +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 + +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 +