力扣原题链接:滑动窗口类型题目
与上两题稍有不同的是这个需要记录每个字符出现的次数,在其他语言中使用哈希表,因为C语言没有哈希表,故定义一个数组代替。
思路:
由动图可知,需要一个数组来记录t中每个字母的频数(tFreq),另需要一个数组来记录滑动窗口中每个包含在t数组中的字母的频数 (winFreq),同时需要一个值来记录窗口中包含在t数组里的各个字母的总数与实际t数组中各个字母的总数的距离(distance)。
代码:
char* minWindow(char* s, char* t) {
//z的ASCII码是122,为了避免越界,设大一点
int winFreq[128] = { 0 };//记录窗口内各个目标字母的个数
int tFreq[128] = { 0 };//记录t数组中各个目标字母的个数
int slowIndex = 0;
int fastIndex = 0;
//假设区间为[slowIndex,fastIndex)
int begin = 0;//定义输出s数组的第一个下标
//记录t中各个字母的频数
int i = 0;
for (; t[i] != '\0'; i++) {
tFreq[t[i]] ++;
}
int sNum = 0;//定义s数组的长度
for (; s[sNum] != '\0'; sNum++);//计算s数组长度
int distance = i;//记录t字符串中剩余没找到的字母的总数
int minLen = sNum + 1;//作为判断是否输出空字符串的依据
for (; s[fastIndex] != '\0'; fastIndex++) {//快指针遍历s数组
char tem = s[fastIndex];
if (tFreq[tem] != 0) {//当遇到t中字母时(因为目标字母的频数tFreq不会等于零)
winFreq[tem] ++;//窗口中目标字母的个数加一
if (winFreq[tem] <= tFreq[tem]) distance--;//当窗口中目标字母的频数还未大于t数组中目标字母频数时,剩余没找到的字母总数差值(distance)减一
}
if (distance == 0) {//当子数组中包含t所有字母
if (minLen > (fastIndex - slowIndex + 1)) {//计算最小长度,并记录begin
minLen = (fastIndex - slowIndex + 1);
begin = slowIndex;
}
if (winFreq[s[slowIndex]] != 0) {//当慢指针所指为t中字母时
if (winFreq[s[slowIndex]] == tFreq[s[slowIndex]]) distance++;
winFreq[s[slowIndex]] --;
slowIndex++;
}
for (; winFreq[s[slowIndex]] == 0; slowIndex++);//慢指针跳到下一个目标字母处
if (slowIndex == fastIndex) continue;//当慢指针与快指针相同时,直接进入下次循环
if (distance == 0 && minLen > (fastIndex - slowIndex + 1)) {//当窗口中包含所有目标字母时,计算最小长度,并记录begin(避免漏去目标字母相邻的情况e.g.s="BQBU" t="BU")
minLen = (fastIndex - slowIndex + 1);
begin = slowIndex;
}
}
}
if (minLen == sNum + 1) return "";//当minLen没变化时,说明不存在子数组,故输出空字符串
char* res = malloc(sizeof(char) * (minLen + 1));
res[minLen] = '\0';//将最后一(minLen-1)有效字母的后一位(minLen)设为终止符,不然老是有后边的奇怪的字符,不懂
int n = 0;
int p = begin;
while (p < begin + minLen) {
res[n ++] = s[p ++];
}
return res;
}