力扣 438.找到字符串中所有字母异位词(C语言)
题目
给定两个字符串 s
和 p
,找到 s
中所有 p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
例如:abc的异位词有(abc,acb,bac,bca,cab,cba)
示例 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的长度)故我们的滑动窗口是固定的,以示例一为例,如下图
由上图可知,该题的滑动窗口在增长到p_len
长度时就固定了,标记字母个数我们则需要靠哈希数组来确定
步骤如下:
-
初始化
s_len
,p_len
,s_hash[256]
,p_hash[256]
,returnSize
(不要忘记它)定义
int * res
并为其分配足够大的内存,这里可以直接确定大小为s_len
(极端情况下,0 ~ (s_len - 1)
的位置都是需要标记的位置) -
滑动窗口开始
- 定义
int left = 0
,int right = 0
,int count = 0
(用于和p_len比较,若两者相等则标记当前left
位置) - 循环
right < s_len
- 判断
s_hash[s[right]] <= p_hash[s[right]]
,若是成立则count++
right
不断增加left
被动增加(当前窗口等于3则需要增加)- 位置标记(通过
count
和p_len
的大小相等来判断,相等标记当前left
位置) - 注意
left
增加前,判断s_hash[s[left]] <= p_hash[s[left]]
,若是成立则count--
left
增加前,需要将s_hash
中当前s[left]
字母对应的计数减 1 ,即s_hash[s[left]]--
left
增加
- 位置标记(通过
- 判断
- 定义
-
返回
res
代码
int* findAnagrams(char * s, char * p, int* returnSize){
int s_len = strlen(s);
int p_len = strlen(p);
//初始化变量,res用于存储索引
int * res = (int *)malloc(sizeof(int) * s_len);
//s_hash用于标记s字符串中的字母,p_hash用于判断s中的字母是否和p中的相等
int s_hash[256] = {0}; int p_hash[256] = {0};
*returnSize = 0;
//初始化p_hash
for(int i=0;i<p_len;i++)
p_hash[p[i]]++;
//滑动窗口
int left = 0;int right = 0;
//用于判断是否是字母异位词
int count = 0;
while(right < s_len)
{
s_hash[s[right]]++;
//相同则使count++,但是s_hash不可超过p_hash,超过则无效
if(s_hash[s[right]] <= p_hash[s[right]])
count++;
right++;
//控制窗口大小
if(right - left == p_len)
{
if(count == p_len)
{
res[(*returnSize)++] = left;
}
//确保最左边的字母是p_hash内标记的元素
if(s_hash[s[left]] <= p_hash[s[left]])
{
count--;
}
//去掉左边第一个元素
s_hash[s[left]]--;
left++;
}
}
return res;
}
注意
- 尤其注意
left
的前进操作 returnSize
记得初始化为0,否则会出现一些奇奇怪怪的错误- 滑动窗口虽然很好理解,但是代码还是需要自己多手敲尝试几遍,
left
的被动前进这一块很容易出问题,不可以大意!
题目:
力扣 438题