给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例 2:
输入:s = "a", t = "a"
输出:"a"
思路
设置双指针进行查找。
滑动窗口的思想:
用i,j表示滑动窗口的左边界和右边界,通过改变i,j来扩展和收缩滑动窗口,可以想象成一个窗口在字符串上游走,当这个窗口包含的元素满足条件,即包含字符串T的所有元素,记录下这个滑动窗口的长度j-i+1,这些长度中的最小值就是要求的结果。
步骤一
不断增加j使滑动窗口增大,直到窗口包含了T的所有元素
步骤二
不断增加i使滑动窗口缩小,因为是要求最小字串,所以将不必要的元素排除在外,使长度减小,直到碰到一个必须包含的元素,这个时候不能再扔了,再扔就不满足条件了,记录此时滑动窗口的长度,并保存最小值
步骤三
让i再增加一个位置,这个时候滑动窗口肯定不满足条件了,那么继续从步骤一开始执行,寻找新的满足条件的滑动窗口,如此反复,直到j超出了字符串S范围。
class Solution {
private:
unordered_map<char, int> umap; //符号,是否使用过
void initMap(string t) {
umap.clear();
for (char ch : t)
umap.insert(make_pair(ch, 0));
}
public:
string minWindow(string s, string t) {
vector<string> str_ret;
int i = 0, j = 0; //窗口游标
for (; i < s.size(); i++) {
int t_cnt = 0; //统计t是否找齐
int cnt=0; //统计umap的和
initMap(t); //初始化map
for (j = i; j < s.size(); j++) {
if (umap.find(s[j]) != umap.end() && umap[s[j]] == 0) { //初次找到字母
umap[s[j]] = umap[s[j]] + 1;
t_cnt = t_cnt + 1;
cnt = cnt + 1;
}
else if (umap.find(s[j]) != umap.end() && umap[s[j]] >= 1) { //找到重复字母
umap[s[j]] = umap[s[j]] + 1;
cnt = cnt + 1;
}
if (t_cnt == t.size()) { //如果找齐了
if (cnt == t_cnt) {
string item;
for (int k = i; k <= j; k++) //构造结果
item += s[k];
str_ret.push_back(item); //添加结果
break;
}
else {
for (; i <= j; i++) { //收缩i
if (umap.find(s[i]) != umap.end() && umap[s[i]] >= 2 && cnt > t_cnt) {
umap[s[i]] = umap[s[i]] - 1;
cnt = cnt - 1;
}
if (cnt == t_cnt) {
string item;
for (int k = i; k <= j; k++) //构造结果
item += s[k];
str_ret.push_back(item); //添加结果
break;
}
}
}
break;
}
}
}
string result;
if (str_ret.empty())
return result;
else {
int min_str = str_ret[0].size();
result = str_ret[0];
for (int k = 1; k < str_ret.size(); k++)
if (str_ret[k].size() < min_str) {
min_str = str_ret[k].size();
result = str_ret[k];
}
}
return result;
}
};
class Solution {
public:
string minWindow(string s, string t) {
vector<int> need(128,0);
int count = 0;
for(char c : t)
need[c]++;
count = t.length();
int l=0, r=0, start=0, size = INT_MAX;
while(r<s.length())
{
char c = s[r];
if(need[c]>0)
count--;
need[c]--; //先把右边的字符加入窗口
if(count==0) //窗口中已经包含所需的全部字符
{
while(l<r && need[s[l]]<0) //缩减窗口
{
need[s[l++]]++;
} //此时窗口符合要求
if(r-l+1 < size) //更新答案
{
size = r-l+1;
start = l;
}
need[s[l]]++; //左边界右移之前需要释放need[s[l]]
l++;
count++;
}
r++;
}
return size==INT_MAX ? "" : s.substr(start, size);
}
};
class Solution {
private:
int cnt=0; //记录need中有
int myConfirm(string item,string s,vector<int>& need) {
vector<int> use(128,0);
for(char ch:item)
use[ch]++;
for(int i=0;i<128;i++)
if(use[i]<need[i])
return s.size()+1;
return item.size();
}
public:
string minWindow(string s, string t) {
vector<int> need(128,0); //用于记录t内有什么样的字母与个数
for(char ch:t) { //记录t中元素
need[ch]++;
cnt++;
}
int begin=0,end=0;
int min_sub=s.size()+1; //记录最小数组
string ret;
if(s.size()<t.size()) //剪枝
return ret;
for(;begin<=s.size()-cnt;begin++) {
string item;
for(end=begin;end<s.size();end++) {
if(end-begin+1>min_sub) //剪枝
break;
item+=s[end];
if(item.size()>=cnt) {
int t=myConfirm(item,s,need); //记录子串长度
if(t<min_sub) {
min_sub=t;
ret=item;
}
}
}
}
return ret;
}
};