/** * https://leetcode.cn/problems/find-all-anagrams-in-a-string/ * @param {string} s * @param {string} p * @return {number[]} */ const findAnagrams = function (s, p) { }; /* 按照暴力解法的话我们可能会把p的所有异位词都枚举出来,之后遍历遍历所有的异位词,利用indexof查看每一个异位词在s中的位置,如果 这个异位词在s中存在,那么就把起始下标放入到结果集中,如果p非常长的话那么所有异位词就是一个长度的全排列!m,加上对s的查找,那就是一个 (n+m)*!m, 这显然是不能接受的,所以我们需要换一种思路. 思路:异位词和原词在单词数量上是一致的,所以我们可以维护一个在s上,长度位p.length的窗口,只要这个窗口里面所有单词出现的数量和 p中所有单词出现的数量一致的话,那么就认为这个窗口表示的单词是p的一个异位词,最后只需把窗口的起始下标放入结果集合即可 */ function f1(s, p) { const result = []; // 返回的结果集 if (s.length < p.length) return result; // p比s还长,在s中不可能有p的异位词,直接返回 const need = new Map(); // 记录p中每一个词出现的次数,用于验证异位词 const window = new Map(); // 记录当前窗口中的异位词 // 统计p的所有字符出现的次数 for (const char of p) { need.set(char, (need.get(char) || 0) + 1); } let left = 0; // 滑动窗口的左边界 let right = 0; // 滑动窗口的右边界 let valid = 0; // 滑动窗口中符合异位字的字符数量; while (right < s.length) { const char = s[right]; if (need.has(char)) { window.set(char, (window.get(char) || 0) + 1); if (window.get(char) === need.get(char)) { valid++; } } // 如果窗口长度等于异位字符p的长度,那么就检测当前窗口是否是一个异位词 if (right - left + 1 === p.length) { if (valid === need.size) { result.push(left); // 把left添加到结果集中 } const leftChar = s[left]; // 收缩窗口 left++; // 判断收缩掉的这个字符是否是need中的某一个字符 if (need.has(leftChar)) { if (window.get(leftChar) === need.get(leftChar)) { valid--; // 验证符合异位词字符个数的词减1 } window.set(leftChar, window.get(leftChar) - 1); // 使这个字符的数量减1 } } // 扩展窗口 right++; } return result; } function f3(s, p) { const result = []; // 返回的结果集 if (s.length < p.length) return result; // p比s还长,在s中不可能有p的异位词,直接返回 const need = new Map(); // 记录p中每一个字符出现的次数,用于验证异位词 const window = new Map(); // 记录当前窗口中的异位词 // 统计p的所有字符出现的次数 for (const char of p) { need.set(char, (need.get(char) || 0) + 1); } let left = 0; // 滑动窗口的左边界 let right = 0; // 滑动窗口的右边界 while (right < s.length) { const char = s[right]; // 扩展窗口 window.set(char, (window.get(char) || 0) + 1); // 如果当前窗口大小达到p的长度,判断是否为异位词 if (right - left + 1 === p.length) { // 比较窗口内字符的频次是否和p中一致 let isAnagram = true; for (const [key, value] of need) { if (window.get(key) !== value) { isAnagram = false; break; } } if (isAnagram) { result.push(left); // 将符合条件的起始索引加入结果 } // 收缩窗口 const leftChar = s[left]; window.set(leftChar, window.get(leftChar) - 1); // 减少左边字符的频次 if (window.get(leftChar) === 0) { window.delete(leftChar); // 如果频次为0,移除该字符 } left++; // 移动左指针 } // 扩展右指针 right++; } return result; }