438. 找到字符串中所有字母异位词(数组、滑动窗口)
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
字母异位词指字母相同,但排列不同的字符串。
不考虑答案输出的顺序。
示例 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” 的字母异位词。
思路
还是有些晕乎,具体见大佬:
https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/solution/hua-dong-chuang-kou-tong-yong-si-xiang-jie-jue-zi-/
遍历子串p,记录各个字母出现的次数;遍历母串s,right先动,遇到和子串中相同的数组,那么就加大窗口,并记录该字符数量;当左右指针相聚子串长度时,要判断子串和母串对应相等的字符数量是不是相等;相等时说明母串中以left开始的串符合子串p排列,将左指针left加入结果数组res;否则就需要将窗口缩小,并且继续遍历
代码
C++版本
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
unordered_map<char, int> need, window;//需求和窗口键值对
vector<int> res;
for(char c : p){
need[c]++;//记录子串中各个字符的个数
}
int left = 0, right = 0;
int valid = 0; int s_len = s.size(); int p_len = p.size();
while(right < s_len){//字符串s的右指针遍历
char c = s[right];//c是将要移入窗口的字符
right++;
// 进行窗口内数据的一系列更新
if(need.count(c)){//子串t中含有元素c,则更新窗口
window[c]++;
if(window[c] == need[c]){//如果窗口中c字符和子串t中相应字符相等
valid++;//标记字符c的数量,valid++
}
}
// 判断左侧窗口是否要收缩
while (right - left >= p_len) {
if(valid == need.size()){//如果窗口中元素和子串t中字符c相等,满足,将起始下标left加入res结果
res.push_back(left);
}
// d 是将移出窗口的字符
char d = s[left];
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
if(need.count(d)){//need数组不为空
if(window[d] == need[d]){
valid--;
}
window[d]--;//不相等说明不为子串排列,窗口-1
}
}
}
return res;
}
};
/*
滑动窗口算法框架C++版
void slidingWindow(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
// 右移窗口
right++;
// 进行窗口内数据的一系列更新
...
// debug 输出的位置
printf("window: [%d, %d)\n", left, right);
// 判断左侧窗口是否要收缩
while (window needs shrink) {
// d 是将移出窗口的字符
char d = s[left];
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}
*/
Java代码
class Solution {
public List<Integer> findAnagrams(String s, String p) {
//需求和窗口键值对
HashMap<Character, Integer> need = new HashMap<>();
HashMap<Character, Integer> window = new HashMap<>();
List<Integer> res = new ArrayList<>();//存储结果
for(Character c : p.toCharArray()){
//当need集合中有这个c时,就使用这个0值;如果没有就使用默认值defaultValue。
need.put(c, need.getOrDefault(c, 0) + 1);//记录子串中各个字符的个数
}
int left = 0, right = 0;
int valid = 0; int s_len = s.length(); int p_len = p.length();
while(right < s_len){//字符串s的右指针遍历
char c = s.charAt(right);//c是将要移入窗口的字符
right++;
// 进行窗口内数据的一系列更新
if( need.containsKey(c) ){//子串t中含有元素c,则更新窗口
window.put( c, window.getOrDefault(c, 0) + 1 );
if( (window.get(c)).equals(need.get(c)) ){//如果窗口中c字符和子串t中相应字符相等,此处如果用==判断太长时通不过
valid++;//标记字符c的数量,valid++
}
}
// 判断左侧窗口是否要收缩
while (right - left >= p_len) {
if(valid == need.size()){//如果窗口中元素和子串t中字符c相等,满足,将起始下标left加入res结果
res.add(left);
}
// d 是将移出窗口的字符
char d = s.charAt(left);
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
if( need.containsKey(d)){
if( (window.get(d)).equals(need.get(d)) ){
valid--;
}
window.put(d, window.get(d) - 1);
}
}
}
return res;
}
}
/*
滑动窗口算法框架Java版
void slidingWindow(string s, string t) {
HashMap<Character, Integer> need = new HashMap<>();
HashMap<Character, Integer> window = new HashMap<>();
List<Integer> res = new ArrayList<>();//存储结果
for(Character c : p.toCharArray()){
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
int valid = 0; int s_len = s.length(); int p_len = p.length();
while (right < s_len) {
// c 是将移入窗口的字符
char c = s.charAt(right);
// 右移窗口
right++;
// 进行窗口内数据的一系列更新
...
// debug 输出的位置
printf("window: [%d, %d)\n", left, right);
// 判断左侧窗口是否要收缩
while (window needs shrink) {
// d 是将移出窗口的字符
char d = s.charAt(left);
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}
*/