438. 找到字符串中所有字母异位词
438. 找到字符串中所有字母异位词
已解答
中等
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
提示:
1 <= s.length, p.length <= 3 * 104s和p仅包含小写字母
题解:
class Solution {public List<Integer> findAnagrams(String s, String p) {List<Integer> ans = new ArrayList<>();int[] cntP = new int[26]; // 统计 p 的每种字母的出现次数int[] cntS = new int[26]; // 统计 s 的长为 p.length() 的子串 s' 的每种字母的出现次数for (char c : p.toCharArray()) {cntP[c - 'a']++; // 统计 p 的字母}for (int right = 0; right < s.length(); right++) {cntS[s.charAt(right) - 'a']++; // 右端点字母进入窗口int left = right - p.length() + 1;if (left < 0) { // 窗口长度不足 p.length()continue;}if (Arrays.equals(cntS, cntP)) { // s' 和 p 的每种字母的出现次数都相同ans.add(left); // s' 左端点下标加入答案}cntS[s.charAt(left) - 'a']--; // 左端点字母离开窗口}return ans;}
}作者:灵茶山艾府
链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/2969498/liang-chong-fang-fa-ding-chang-hua-chuan-14pd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
核心思路:两个字符串是异位词的充要条件是:它们包含的字符种类和每种字符的数量完全相同。例如"abc"和"cba"是异位词(都有 1 个a、1 个b、1 个c),而"abd"和"abc"不是(字符d和c不同)。
使用两个长度为 26 的数组(对应 26 个小写字母):
cntp:记录字符串p中每个字符的出现次数(如cntp[0]表示'a'的数量,cntp[1]表示'b'的数量,以此类推)。cnts:记录当前滑动窗口内(s的子串)每个字符的出现次数。
代码关键解读
步骤 1:初始化cntp数组
遍历p的每个字符,统计其出现次数并存储到cntp中:
for (char c : p.toCharArray()) {cntp[c - 'a']++; // 'a'-'a'=0,'b'-'a'=1,映射到数组索引
}
步骤 2:滑动窗口遍历s,实时维护cnts数组
用right指针从 0 开始遍历s,同时通过left指针维护一个长度为p.length()的滑动窗口:
- 扩展窗口右侧:将
s[right]加入当前窗口,更新cnts数组(对应字符计数 + 1):
cnts[s.charAt(right) - 'a']++;
- 确定窗口左侧边界:当
right足够大时(窗口长度等于p的长度),计算left:
int left = right - p.length() + 1; // 窗口起始索引 = 结束索引 - 窗口长度 + 1
- 判断窗口是否有效:
-
- 若
left < 0:窗口长度不足p.length(),继续扩展右侧(不做判断)。 - 若
left >= 0:窗口长度恰好等于p.length(),此时比较cnts和cntp:
- 若
-
-
- 若两者相等:当前窗口是
p的异位词,记录left到结果列表。 - 无论是否相等,都需要将窗口左侧的字符移出(
cnts对应计数 - 1),为下一次窗口滑动做准备:
- 若两者相等:当前窗口是
-
cnts[s.charAt(left) - 'a']--; // 移除窗口左侧字符
