题目
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = “ADOBECODEBANC”, T = “ABC” 输出: “BANC” 说明:
如果 S 中不存这样的子串,则返回空字符串 “”。 如果 S 中存在这样的子串,我们保证它是唯一的答案。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析
像这种字符串子串的问题,脑海里出现了两个声音:动态规划、滑动窗口。
很明显,这道题目适合用滑动窗口来解决。我只需要用两个指针,初始时左指针在最左边,右指针指向第一个满足条件的串(如测试样例中,一开始又指针就指向第一个C),统计串满足条件时的长度,然后移动左指针,直到删掉了某个“必须字符”,这时开始不断右移指针,直到重新包含这个“必须字符”。如此交替移动左右指针,就可以完成任务。
以S = “ADOBECODEBANC”, T = "ABC"为例。
初态left指向第一个A(坐标0),right指向第一个C(坐标5),长度为6;
右移left,直到指向下一个“必须字符”,也就是第一个B(坐标3),此时窗口中减少的“必须字符”是A,右移指针直到找到A(下标11),此时长度为9;
重新开始右移left,指向下一个“必须字符”,也就是第一个C(坐标5),此时左指针删去的B因为不是必须字符(上一次右指针移动的时候经过过其它B),可以保持右指针不动,直接统计长度,长度为7.
重新开始右移left,指向下一个“必须字符”,也就是第二个B(坐标9),此时左指针删去的是必要字符C,右移右指针指向最后一个C(坐标12),长度为4.
因此本题所求的最小长度是4.
至于如何判断是否是“必须字符”,就用hashmap统计字幕出现的次数,如果次数大于必须的次数,就不是“必须字符”,否则就认为是必须字符。
依照此种算法,可得代码:
代码
char * minWindow(char * s, char * t){
if (s == NULL || t == NULL) {
return NULL;
}
int len = (int)strlen(s);
int lent = (int)strlen(t);
char *out = (char *)malloc(len + 1);
if (out == NULL) {
return NULL;
}
int outLen = 0;
memset(out, 0, len + 1);
if (len < lent || lent == 0) {
return out;
}
int map[128] = {0};
int mapt[128] = {0};
int cnt = 0;
/* 1. 初始窗口是短串长度, 先遍历一遍 得到长串中能匹配到短串的字符个数 */
for (int i = 0; i < lent; i++) {
mapt[t[i]]++;
map[s[i]]++;
}
for (int i = 0; i < 128; i++) {
if (mapt[i] > 0) {
/* 临界点,只要 <= 短串 就是有效计数, 再大就是多余的了 */
cnt += (map[i] >= mapt[i]? mapt[i] : map[i]);
}
}
/* 2. 当计数等于短串长度就是满足条件了 */
if (cnt == lent) {
outLen = lent;
memcpy(out, s, outLen);
return out;
}
/* 3. 滑动窗口 */
int l = 0;
int r = lent;
for (; r < len; r++) {
if (mapt[s[r]] == 0) {
continue;
}
map[s[r]]++;
if (map[s[r]] <= mapt[s[r]]) {
/* 临界点,只要 <= 短串 就是有效计数, 再大就是多余的了 */
cnt++;
}
if (cnt == lent) {
/* 从左侧开始校验 找到临界点(map[s[l]] == mapt[s[l]])删除多余数据 */
while (mapt[s[l]] == 0 || map[s[l]] > mapt[s[l]]) {
map[s[l++]]--;
}
int cpLen = r - l + 1;
if (outLen == 0 || cpLen < outLen) {
memset(out, 0, len + 1);
memcpy(out, s + l, cpLen);
outLen = cpLen;
}
/* 临界点 */
cnt--;
map[s[l++]]--;
}
}
return out;
}
运行结果
结果已经比较理想了。
最近在学Java,预计近期会发几篇Java相关的学习内容。
感谢大家的支持和点赞。