找到字符串中所有字母异位词
难度:
标签:
题目描述
给定两个字符串 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
仅包含小写字母
代码结果
运行时间: 61 ms, 内存: 16.5 MB
/*
* 思路:
* 使用滑动窗口算法和Java Stream来解决这个问题。我们首先计算字符串 p 的字符频率,
* 然后我们在字符串 s 上滑动窗口,每次移动一个字符,并更新窗口内字符的频率。
* 使用Java Stream的API来简化频率表的比较和更新操作。
*/
import java.util.*;
import java.util.stream.Collectors;
public class SolutionStream {
public List<Integer> findAnagrams(String s, String p) {
if (s.length() < p.length()) return Collections.emptyList();
Map<Character, Long> pCount = p.chars().mapToObj(c -> (char)c)
.collect(Collectors.groupingBy(c -> c, Collectors.counting()));
Map<Character, Long> sCount = s.substring(0, p.length()).chars().mapToObj(c -> (char)c)
.collect(Collectors.groupingBy(c -> c, Collectors.counting()));
List<Integer> result = new ArrayList<>();
if (sCount.equals(pCount)) {
result.add(0);
}
for (int i = p.length(); i < s.length(); i++) {
char newChar = s.charAt(i);
char oldChar = s.charAt(i - p.length());
sCount.put(newChar, sCount.getOrDefault(newChar, 0L) + 1);
sCount.put(oldChar, sCount.get(oldChar) - 1);
if (sCount.get(oldChar) == 0) {
sCount.remove(oldChar);
}
if (sCount.equals(pCount)) {
result.add(i - p.length() + 1);
}
}
return result;
}
}
解释
方法:
该题解采用了滑动窗口的方法。首先,使用一个哈希表 need 记录字符串 p 中每个字符的出现次数。然后,使用另一个哈希表 window 来记录当前窗口中各字符的出现次数。接着,不断扩展右边界 right,直到窗口中包含了 p 中所有字符。此时,开始收缩左边界 left,直到窗口中不再包含 p 的所有字符。在收缩的过程中,如果窗口的长度恰好等于 p 的长度,则说明找到了一个异位词的起始索引,将其添加到结果列表中。重复这个过程,直到遍历完字符串 s。
时间复杂度:
O(n)
空间复杂度:
O(p)
代码细节讲解
🦆
为什么选择使用滑动窗口技术来解决这个问题,而不是其他算法例如排序比较或者使用Trie树?
▷🦆
在哈希表`need`中,如果某个字符在字符串`p`中出现多次,你是如何处理这种情况的?
▷🦆
在滑动窗口中,当`valid`等于`need`的长度时,为什么可以认为窗口内的字符串是一个有效的异位词?
▷🦆
你的算法在处理非常大的字符串`s`时的效率如何?是否有可能出现性能瓶颈?
▷