Algorithms first
串联所有单词的子串 · 交互式算法学习
找出由所有单词恰好串联形成的子串起点。
#30 · 滑动窗口
串联所有单词的子串
Substring with Concatenation of All Words
s = "barfoothefoobarman", words = ["foo","bar"]线性序列4 个关键状态
步骤 1偏移 0,窗口加入 bar。
count=1bar
0foo
1the
2foo
3bar
4man
5推荐
按单词长度分组滑窗
时间
O(n)空间 O(k)对每个字符偏移维护单词窗口,超频时从左侧删除单词。
1function findSubstring(s, words) {2 const w = words[0].length,3 k = words.length,4 need = new Map(),5 out = [];6 for (const x of words) need.set(x, (need.get(x) || 0) + 1);7 for (let offset = 0; offset < w; offset++) {8 let l = offset,9 count = 0,10 seen = new Map();11 for (let r = offset; r + w <= s.length; r += w) {12 const x = s.slice(r, r + w);13 if (!need.has(x)) {14 seen.clear();15 count = 0;16 l = r + w;17 continue;18 }19 seen.set(x, (seen.get(x) || 0) + 1);20 count++;21 while (seen.get(x) > need.get(x)) {22 const y = s.slice(l, l + w);23 seen.set(y, seen.get(y) - 1);24 l += w;25 count--;26 }27 if (count === k) out.push(l);28 }29 }30 return out;31}多语言参考
来源 · CC BY-SA 4.0 ↗Java · C++ · Go
这是一组同题正确实现,独立于上方当前动画方法;不同语言可能采用另一种正确策略。
1class Solution {2 public List<Integer> findSubstring(String s, String[] words) {3 Map<String, Integer> cnt = new HashMap<>();4 for (var w : words) {5 cnt.merge(w, 1, Integer::sum);6 }7 List<Integer> ans = new ArrayList<>();8 int m = s.length(), n = words.length, k = words[0].length();9 for (int i = 0; i < k; ++i) {10 int l = i, r = i;11 Map<String, Integer> cnt1 = new HashMap<>();12 while (r + k <= m) {13 var t = s.substring(r, r + k);14 r += k;15 if (!cnt.containsKey(t)) {16 cnt1.clear();17 l = r;18 continue;19 }20 cnt1.merge(t, 1, Integer::sum);21 while (cnt1.get(t) > cnt.get(t)) {22 String w = s.substring(l, l + k);23 if (cnt1.merge(w, -1, Integer::sum) == 0) {24 cnt1.remove(w);25 }26 l += k;27 }28 if (r - l == n * k) {29 ans.add(l);30 }31 }32 }33 return ans;34 }35}交互式算法学习
从执行步骤真正理解 LeetCode 经典 150
本站整理 150 道高频算法面试题和 302 种解法。动画方法同步展示 JavaScript 与 Python;每题另提供 Java、C++ 与 Go 同题参考实现。
AlgoViz Lab
串联所有单词的子串 · 解法对比
找出由所有单词恰好串联形成的子串起点。
- 测试用例
s = "barfoothefoobarman", words = ["foo","bar"]- 题目分类
- 滑动窗口
- 解法对比
- 2
每个起点拆块计数
从每个可能起点切出 k 个等长单词,并比较频次。
- 时间
O(n·k)- 空间
O(k)
按单词长度分组滑窗
对每个字符偏移维护单词窗口,超频时从左侧删除单词。
- 时间
O(n)- 空间
O(k)