题目描述
给定两个字符串
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 * 104
s
和p
仅包含小写字母
解题思路
为了解决这个问题,我们可以使用滑动窗口和哈希表的方法。滑动窗口可以动态地维护一个长度为 p.length()
的子串,同时我们可以通过比较该子串和 p
是否为异位词来判断是否记录其起始索引。具体步骤如下:
-
构建目标频率表:我们首先统计字符串
p
中每个字符的出现频率,保存到一个数组pFreq
中。 -
初始化滑动窗口:使用两个指针
left
和right
表示滑动窗口的左右边界。初始时,left
和right
都指向字符串s
的起始位置。另外,用一个数组sFreq
来统计当前窗口中字符的频率。 -
滑动窗口遍历:每次将
right
指向的字符加入sFreq
,并移动right
指针。当窗口大小等于p.length()
时,比较sFreq
和pFreq
是否相等,如果相等则说明该窗口是p
的一个异位词,将left
的位置加入结果列表。然后,移动left
指针,并减少left
指向字符的频率,继续下一轮滑动。 -
终止条件:当
right
指针遍历到字符串s
的末尾时,遍历结束。
复杂度分析
-
时间复杂度:O(n)。其中
n
是字符串s
的长度。滑动窗口每次移动都需要比较两个频率表,这一步是 O(1) 的操作,因此整个算法的时间复杂度为 O(n)。 -
空间复杂度:O(1)。虽然我们使用了两个频率表
pFreq
和sFreq
,但它们的大小是固定的(26个字母),所以空间复杂度为 O(1)。
代码实现
package org.zyf.javabasic.letcode.hot100.slidingwindow;
import java.util.ArrayList;
import java.util.List;
/**
* @program: zyfboot-javabasic
* @description: 找到字符串中所有字母异位词
* @author: zhangyanfeng
* @create: 2024-08-21 21:26
**/
public class FindAnagramsSolution {
public List<Integer> findAnagrams(String s, String p) {
// 结果列表
List<Integer> result = new ArrayList<>();
// 特殊情况处理
if (s.length() < p.length()) {
return result;
}
// 统计字符串 p 中每个字符的频率
int[] pFreq = new int[26];
for (char c : p.toCharArray()) {
pFreq[c - 'a']++;
}
// 滑动窗口的字符频率
int[] sFreq = new int[26];
// 初始化滑动窗口
int left = 0, right = 0;
while (right < s.length()) {
// 将当前字符加入窗口的频率统计
sFreq[s.charAt(right) - 'a']++;
// 当窗口大小达到 p 的长度时,开始检查
if (right - left + 1 == p.length()) {
// 检查当前窗口是否为异位词
if (matches(sFreq, pFreq)) {
result.add(left);
}
// 移动左指针,缩小窗口,更新频率表
sFreq[s.charAt(left) - 'a']--;
left++;
}
// 右指针继续向右扩展窗口
right++;
}
return result;
}
// 辅助函数:检查两个频率数组是否相同
private boolean matches(int[] sFreq, int[] pFreq) {
for (int i = 0; i < 26; i++) {
if (sFreq[i] != pFreq[i]) {
return false;
}
}
return true;
}
public static void main(String[] args) {
FindAnagramsSolution solution = new FindAnagramsSolution();
// 测试用例 1
String s1 = "cbaebabacd";
String p1 = "abc";
System.out.println(solution.findAnagrams(s1, p1)); // 输出: [0, 6]
// 测试用例 2
String s2 = "abab";
String p2 = "ab";
System.out.println(solution.findAnagrams(s2, p2)); // 输出: [0, 1, 2]
}
}
具体可参考:https://zyfcodes.blog.csdn.net/article/details/141401712