class Solution {
public List<Integer> findAnagrams(String s, String p) {
int len1 = s.length();
int len2 = p.length();
if(len1<len2){
return new ArrayList<Integer>();
}
List<Integer> ans = new ArrayList<Integer>();
int[] sCount = new int[26];
int[] pCount = new int[26];
for(int i = 0;i<len2;i++){
sCount[s.charAt(i) - 'a']++;
pCount[p.charAt(i) - 'a']++;
}
if(Arrays.equals(sCount,pCount)){
ans.add(0);
}
for(int i = 0;i<len1-len2;i++){
sCount[s.charAt(i)-'a']--;
sCount[s.charAt(i+len2)-'a']++;
if(Arrays.equals(sCount,pCount)){
ans.add(i+1);
}
}
return ans;
}
}
主要步骤解释:
-
输入参数与边界检查:
len1
和 len2
分别是字符串s
和p
的长度。- 如果
s
的长度比p
短,那么显然不可能存在字母异位词,直接返回一个空列表。
-
初始化计数数组:
sCount
和pCount
是两个长度为 26 的数组,用来记录当前窗口内和字符串p
的每个字母的出现频率。这里假设字母都是小写字母,因此每个字母可以通过char - 'a'
映射到数组中的索引。- 首先,遍历
p
的长度,将p
中每个字母的出现频率记录在pCount
中,同时初始化s
中前pLen
个字母的频率到sCount
中。
-
初始比较:
- 如果初始化后的
sCount
和pCount
相等,说明s
的起始部分(长度为 len2
)是一个字母异位词。此时将起始索引 0 加入答案列表ans
中。
- 如果初始化后的
-
滑动窗口遍历字符串
s
:- 从
s
的第 len2 个字符开始,利用滑动窗口方法,依次移动窗口:- 移除窗口的第一个字符,即窗口左侧的字符,将它从
sCount
中对应字母的频率减一。 - 添加新的字符(窗口右侧新加入的字符),将它的频率加一。
- 移除窗口的第一个字符,即窗口左侧的字符,将它从
- 在每次调整窗口后,检查
sCount
是否与pCount
相等,若相等,则当前窗口中的子串是一个字母异位词,将起始索引加入ans
中。
- 从
-
返回结果:
- 最后返回
ans
,其中包含了所有找到的字母异位词子串的起始索引。
- 最后返回
核心思想:
这个算法的核心是利用“滑动窗口”的思想来比较子串是否是字母异位词。通过每次滑动窗口时更新频率计数数组,我们可以在 O(n)
的时间复杂度内解决问题,而不需要每次移动窗口都重新计算整个子串的频率。