35
这道题的思路是:
1) begin开始指向0, end一直后移,直到begin - end区间包含T中所有字符。
记录窗口长度d
2) 然后begin开始后移移除元素,直到移除的字符是T中的字符则停止,此时T中有一个字符没被
包含在窗口,
3) 继续后移end,直到T中的所有字符被包含在窗口,重新记录最小的窗口d。
4) 如此循环知道end到S中的最后一个字符。
时间复杂度为O(n)
public class Solution {
public String minWindow(String S, String T) {
int[] map = new int[128];
//init map, 记录T中每个元素出现的次数
for(int i = 0; i < T.length(); i++) {
map[T.charAt(i)]++;
}
// begin end两个指针指向窗口的首位,d记录窗口的长度, counter记录T中还有几个字符没被窗口包含
int begin = 0, end = 0, d = Integer.MAX_VALUE, counter = T.length(), head = 0;
// end指针一直向后遍历
while(end < S.length()) {
// map[] > 0 说明该字符在T中出现,counter-- 表示对应的字符被包含在了窗口,counter--, 如果s中的字符没有在T中出现,则map[]中对应的字符-1后变为负值
if(map[S.charAt(end++)]-- > 0) {
counter--;
}
// 当counter==0时,说明窗口已经包含了T中的所有字符
while (counter == 0) {
if(end - begin < d) {
d = end - (head = begin);
}
if(map[S.charAt(begin++)]++ == 0) { // begin开始后移,继续向后寻找。如果begin后移后指向的字符在map中==0,表示是在T中出现的,如果没有出现,map[]中的值会是负值。
counter++; // 在T中的某个字符从窗口中移除,所以counter++。
}
}
}
return d==Integer.MAX_VALUE ? "" :S.substring(head, head+d);
}
}
编辑于 2017-09-13 02:04:13
回复(15)
5
class Solution {
public:
//维持一个窗口滑动,左边是left,右边是right,然后判断是否包含T
string minWindow(string S, string T)
{
string result;
if(!S.size() || !T.size())
{
return result;
}
mapTmap;//存储T字符串里字符,便于与S匹配
int left = 0;//左边窗口,起始为0
int count = 0;//计数器,对窗口内T串字符数量进行计数,起始为0
//装载T串
int minlen = S.size() + 1;//最小窗口,便于比较最后取最小的,初始化取最大
for(int i = 0; i < T.size(); ++i)
{
Tmap[T[i]]++;
}
//移动右边窗口
for(int right = 0; right < S.size(); ++right)
{
if(Tmap.find(S[right]) != Tmap.end())//当窗口内部有T中的字符
{
if(Tmap[S[right]] > 0)
{
count++;//计数器+1
}
Tmap[S[right]]--;//去掉包含的,剩下都是没包含的
while(count == T.size())//当窗口内有T的所有字符,就要开始移动左窗口啦
{
if(Tmap.find(S[left]) != Tmap.end())
{
//好了,这就是一个包含字符串的窗口范围:left ~ right,
//判断是否比当前窗口还小,是就取串
if(minlen > right - left + 1)
{
//更新窗口大小
minlen = right -left + 1;
result = S.substr(left, right - left + 1);
}
//舍弃窗口左边字符串,继续移动窗口
Tmap[S[left]]++;
if(Tmap[S[left]] > 0)//如果左边连续相同,则count不递减,窗口需要继续向右移动
{
count--;
}
}
left++;//移动左窗口
}
}
}
return result;
}
};
编辑于 2017-07-30 21:07:56
回复(7)
3
class Solution {
public:
string minWindow(string S, string T) {
string result;
mapt,s;
for(auto c:T)
t[c]++;
int count=0,l=0;
for(int r=0;r
{
if(t[S[r]] != 0)
{
s[S[r]]++;
if(s[S[r]] <= t[S[r]])
count++;
while(count == T.length())
{
if(result.empty() || result.length()>r-l+1)
result = S.substr(l,r-l+1);
if(t[S[l]])
{
s[S[l]]--;
if(s[S[l]] < t[S[l]])
count--;
}
l++;
}
}
}
return result;
}
};
发表于 2017-09-05 01:58:33
回复(1)
1
class Solution {
public:
string minWindow(string S, string T) {
int t[127] = {0};
for (int i = 0; i
int ok = 0;
bool vis[127] = {0};
for (int i = 0; i
int mi = 1e9, mi_l = 0;
for (int i = 0, j = 0; i
for ( ; j
if (vis[S[j]]) {
if (t[S[j]] == 1) --ok; //字母S[j]不需要了
--t[S[j]];
}
}
if (ok == 0 && mi > j - i) {
mi = j - i;
mi_l = i;
}
//去掉S[i]
if (vis[S[i]]) {
if (t[S[i]] == 0) ++ok; //字母S[i]不够了
++t[S[i]];
}
}
if (mi == 1e9) return "";
else return S.substr(mi_l, mi);
}
};
发表于 2020-07-12 17:44:57
回复(0)
1
//主要思路是通过两次遍历找到所有可能的窗口(即从S中从start到end包含一个T),通过以下几个步骤实现:
//为了减少时间复杂度,用map记录T中所有字符出现的次数,使用count在S中寻找时计数,一旦找到一个T中的字符
//就count++,同时map中当前字符的次数减1,当count等于T的长度时,即找到了一个包含T的字符串,然后
//通过比较每个找到的字符串的长度,选择最短字符串,即为题中所求
class Solution {
public:
string minWindow(string S, string T) {
int len = T.size();
int count = 0;
int lenS = S.size();
int start=0,end=lenS;
for(int i=0;i
{
map mp;
for(int i=0;i
mp[T[i]] += 1;
if(mp[S[i]]>0)
{
count = 0;
for(int j=i;j
{
if(mp[S[j]]>0)
{
count++;
mp[S[j]]--;
}
if(count==len)
{
if(j-i
{
start = i;
end = j;
}
break;
}
}
}
}
if(start>=0 && end
return S.substr(start,end-start+1);
return "";
}
};
发表于 2019-01-14 16:07:51
回复(2)
1
class Solution {
public:
string minWindow(string s, string t) {
int len = t.length();
int size = s.length();
if(size < len){
return "";
}
int record[256] = {0};
int freq[256] = {0};
for(int i = 0; i < len; ++i){
++record[t[i]];
}
int i = 0;
int j = -1;
int sum = 0;
int min_len = size;
int cur = -1;
while(i < size){
if(sum < len && j+1 < size){
++freq[s[++j]];
if(record[s[j]] && freq[s[j]] <= record[s[j]]){
++sum;
}
} if(sum >= len){
if(min_len >= j-i+1){
cur = i;
min_len = j-i+1;
}
--freq[s[i]];
if(record[s[i]] && record[s[i]] - freq[s[i]] >= 1){
--sum;
}
++i;
}
if(j == size-1 && sum < len)
break;
}
if(cur != -1){
return string(s, cur, min_len);
}else{
return "";
}
}
};
发表于 2018-04-03 00:14:05
回复(0)
1
//暴力求吧....感觉没啥好方法
class Solution {
public:
string minWindow(string S, string T) {
int MIN=0x7FFFFFFF;
string res="";
for(int i=0;i
{ int Cur=0;
string temp=T;
for(int j=i;j
{
for(int k=0;k
{
if(S[j]==temp[k])
{
temp[k]='*';
Cur++;
break;
}
}
if(Cur==T.size())
{
if(j-i+1
{
MIN=j-i+1;
res=S.substr(i,j-i+1);
}
break;
}
}
}
return res;
}
};
编辑于 2018-03-24 13:58:40
回复(0)
1
T有可能重复,所以需要对T中每个字符都要判断是否满足次数 class Solution(object):
def minWindow(self, s, t):
res_begin = -1
res_len = 2147483647
m = {}
m_needed = {}
for i in range(len(t)):
m[t[i]] = 0
if t[i] not in m_needed:
m_needed[t[i]] = 1
else:
m_needed[t[i]] += 1
count = 0
begin = 0
end = 0
while True:
if count < len(m_needed):
if end == len(s):
break
if s[end] in m:
m[s[end]] += 1
if m[s[end]] == m_needed[s[end]]:
count += 1
end += 1
else:
if res_len > end - begin:
res_begin = begin
res_len = end - begin
if s[begin] in m:
m[s[begin]] -= 1
if m[s[begin]] == m_needed[s[begin]] - 1:
count -= 1
begin += 1
if res_begin == -1:
return ""
else:
return s[res_begin:res_begin + res_len]
发表于 2017-10-09 13:02:12
回复(0)
1
class Solution {
public:
string minWindow(string S, string T) {
// 思路是在S中选一个T中的元素作为起点,来计算最短窗口
if(S == ""|| T == "")
return "";
int tlen = T.size();
int slen = S.size();
if(tlen > slen)
return "";
multiset Tset; // 存T中的元素
for(int i = 0; i < tlen; i++)
Tset.insert(T[i]);
int min = 10000; // 最短窗口长度
int bindex = 10000; // 窗口的起点
for(int i = 0; i <= slen-tlen; i++)
{
if(Tset.find(S[i]) == Tset.end()) // 找一个起点
continue;
int begindex = i;
multiset Ts = Tset;
int j = begindex;
for( ;!Ts.empty() && j < slen;j++)
{
auto iter = Ts.find(S[j]);
if(iter != Ts.end())
Ts.erase(iter);
// 直接用Ts.erase(S[j])会删掉所有值为S[j]的节点
}
if(Ts.empty())
{
int temp = j-begindex;
if(min > temp) // 更新最短长度
{
min = temp;
bindex = begindex;
}
}
}
if(bindex == 10000)
return "";
return S.substr(bindex,min);
}
};
编辑于 2017-08-18 14:45:06
回复(0)
1
import java.util.HashMap;
import java.util.Map;
public class Solution {
public String minWindow(String s, String t) {
if (s == null || t == null || s.length() < t.length())
return "";
// HashMap的key为t中各个字符,value为对应字符个数
Map map = new HashMap();
for (char c : t.toCharArray()) {
if (!map.containsKey(c))
map.put(c, 0);
map.put(c, map.get(c) + 1);
}
// minLeft为最小窗口左下标,minLen为最小长度,count用来计数
int minLeft = 0, minLen = s.length() + 1, count = 0;
int left = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (map.containsKey(c)) {
// 如果map.get(c)说明t中还有字符没有包含,计数器+1
if (map.get(c) > 0){
count++;
}
map.put(c, map.get(c) - 1);
}
// 如果left到i中包含t中所有字符
while (count == t.length()) {
if (i - left + 1 < minLen) {
minLeft = left;
minLen = i - left + 1;
}
c = s.charAt(left);
if (map.containsKey(c)) {
map.put(c, map.get(c) + 1);
if (map.get(c) > 0)
count--;
}
left++;
}
}
if (minLen > s.length())
return "";
return s.substring(minLeft, minLeft + minLen);
}
}
发表于 2017-07-25 20:06:09
回复(1)
1
import java.util.*;
public class Solution {
public String minWindow(String S, String T) {
String res = "";
HashMap map = new HashMap();
int left = 0;
int minL = Integer.MAX_VALUE;
int count = 0;
for(int i=0; i
if(map.containsKey(T.charAt(i)))
map.put(T.charAt(i), map.get(T.charAt(i))+1);
else
map.put(T.charAt(i), 1);
}
for(int i=0; i
if(map.containsKey(S.charAt(i))) {
map.put(S.charAt(i), map.get(S.charAt(i))-1);
if(map.get(S.charAt(i)) >= 0) {
count++;
}
while(count == T.length()) {
if(map.containsKey(S.charAt(left))) {
if(i - left + 1 < minL) {
minL = i - left + 1;
res = S.substring(left, i+1);
}
map.put(S.charAt(left), map.get(S.charAt(left))+1);
if(map.get(S.charAt(left)) > 0)
count--;
}
left++;
}
}
}
return res;
}
}
编辑于 2017-06-23 12:34:10
回复(0)
1
贴个Java的,参照了某位同志的思路
public class Solution {
// 从头开始检查S,如果是T中的元素,计算如果以该元素作为窗口的第一个元素
public String minWindow(String S, String T) {
if(S == null || S.length() <= 0 || T == null || T.length() <= 0)
return "";
int[] sCount = new int[256];
int[] tCount = new int[256];
for(int i = 0; i < T.length(); i++){
tCount[(int)T.charAt(i)]++;
}
int begin = 0, e_begin = 0;
int end = S.length(), e_end = S.length();
for(int i = 0; i < S.length(); i++){
// 计算以S.charAt(i)开头的最小窗口
// S.charAt(i)不是T中字符,肯定不会是开头
if(tCount[S.charAt(i)] == 0)
continue;
sCount[S.charAt(i)]++;
end = i;
boolean isFind = true;
for(int j = 0; j < 256; j++){
if(sCount[j] < tCount[j]){
isFind = false;
break;
}
}
// 找到了T中所有字符
if(isFind){
// 找到S中包含T中字符的开头
while(begin < S.length()){
int ch = S.charAt(begin);
if(tCount[ch] == 0){
begin++;
continue;
}
// 如果ch出现次数超了,那么开头指针往后
if(sCount[ch] - 1 >= tCount[ch]){
sCount[ch]--;
begin++;
}
else {
break;
}
}
// 更新最小窗口的长度
if(e_end - e_begin > end - begin){
e_end = end;
e_begin = begin;
}
}
}
if(e_end - e_begin >= S.length())
return "";
else
return S.substring(e_begin, e_end + 1);
}
}
发表于 2017-05-09 15:22:08
回复(0)
0
滑动窗口: 注释详尽的题解
class Solution {
public:
/**
*
* @param S string字符串
* @param T string字符串
* @return string字符串
*/
string minWindow(string S, string T) {
// write code here
int ns = S.length();
int minl = 0x3f3f3f3f;
int start = 0;
Book book = Book(T);
// 双指针
int l = 0, r = 0;
while(l
start = l;
r = l;
while(r
book.insert(S[r]);
// 判断该区间是否符合条件
while(book.full()){
if(r - l + 1
minl = r - l + 1;
start = l;
}
// 然后缩短左端点
book.erase(S[l++]);
// 跳过无效字符
while(l
}
r++;
}
if(minl == 0x3f3f3f3f) return "";
else return S.substr(start,minl);
}
private:
class Book{
private:
int include[128]; // 每种字符包含的个数
int cnt = 0; // 包含字符的总数
int vis[128]; // 当前访问到的字符总数
int vis_cnt = 0; // 当前访问的有效字符数
public:
Book(string& s){
cnt = s.length();
memset(include,0,sizeof(include));
memset(vis,0,sizeof(vis));
vis_cnt = 0;
for(int i = 0;i
include[s[i]]++;
}
// 插入一个新字符
void insert(char c){
if(include[c] > 0){
vis[c]++;
// 有效的字符
if(vis[c] <= include[c]) vis_cnt++;
}
}
// 删除一个新字符
void erase(char c){
if(include[c] > 0){
vis[c]--;
if(vis[c]
}
}
// 是否是有效的字符
bool included(char c){
return include[c] > 0;
}
// 判断是否包含了所有字符
bool full(){
return (vis_cnt == cnt);
}
};
};
发表于 2021-02-17 16:10:55
回复(0)
0
public String minWindow (String S, String T) {
String str="";
String tmp=S;
if(S.isEmpty()||T.isEmpty())
return str;
if(S.contains(T))
return T;
for(int i=0;i
String s=T.substring(i, i+1);
if(!S.contains(s))
return str;
tmp=tmp.replaceFirst(s, " ");
}
int start=tmp.indexOf(" ");
int end=tmp.lastIndexOf(" ");
if(start>end-T.length())
return str;
str=S.substring(start,end+1);
while(true) {
String s=S.substring(start, start+1);
if(!tmp.contains(s))
break;
tmp=tmp.replaceFirst(s, " ");
start=tmp.indexOf(" ",start+1);
end=tmp.lastIndexOf(" ");
if(end+1-start
str=S.substring(start,end+1);
}
return str;
}
发表于 2020-12-15 02:14:01
回复(0)
0
//时间复杂度O(n); 基本思路,左指针从左往右遍历,找到存在于T中的元素,右指针开始向右边移动直到全部包含T;更新左指针到下一个存在于T的元素,如此反复;找到min
class Solution {
public:
string minWindow(string S, string T) {
// write code here
int left=0;
int count=0;
int slen=S.size();
int tlen=T.size();
int min=slen+1;
count=0; //包含全部的判断
map hash;
for(auto k:T)
{
hash[k]++;
}
int i=0;
int j=i;
for(;i
{
//找第一个T中的字符
if(hash.find(S[i])!=hash.end())
{
//开始移动右指针
while(j
{
if(hash.find(S[j])!=hash.end())
{
if(hash[S[j]]>0)
{
count++;
}
hash[S[j]]--; //存在就减
if(count==tlen) //全部包含时候
{
if(j-i+1
{
left=i;
min=j-i+1;
}
break;
}
}
j++;
}
if(count!=tlen) break;
//从0开始找到了第一个包含t的字串
if(hash[S[i]]==0) count--;
hash[S[i]]++;
hash[S[j]]++;
count--;
}
}
if(min==slen+1) return "";
return S.substr(left,min);
}
};
发表于 2020-12-09 15:04:40
回复(0)
0
/**
* 由于存在重复,使用两个MAP,一个来记录出现的次数,一个记录当前串里存在的次数
* 使用双指针,产生滑动窗口,右边指针不断右移,满足条件后(找到了子串),左边开始右移
* 需要记录最合适的变量参数,start和len,作为最终值,其它都是过程参数
*/
public static String minWindow (String S, String T) {
Map m = new LinkedHashMap();
Map n = new LinkedHashMap();
for (char c: T.toCharArray()) {
n.put(c, n.getOrDefault(c, 0) + 1);
}
int right = 0, left = 0;
int target = 0;
int start = 0;
int len = S.length();
boolean find = false;
while (right < S.length()) {
char c = S.toCharArray()[right];
right++;
if (n.get(c) != null) {
m.put(c, m.getOrDefault(c, 0) + 1);
if (m.get(c) == n.get(c)) {
target++;
}
}
while (target == n.size()) {
if (right - left < len) {
len = right - left;
start = left;
}
find = true;
char s = S.toCharArray()[left];
left++;
if (n.get(s) != null) {
if (m.get(s) == n.get(s)) {
target--;
}
m.put(s, m.getOrDefault(s, 0) - 1);
}
}
}
return find ? S.substring(start, start + len) : "";
}
发表于 2020-12-08 15:34:45
回复(0)
0
#include
class Solution {
public:
string minWindow(string S, string T) {
// write code here
unordered_map mp,num;
for(auto x : T) mp[x]++;
int l = 0,r = -1,cnt = 0,n = S.size(), m = T.size();
int minL = -1, minLength = INT_MAX;
while(r
while(r
if(++num[S[++r]] <= mp[S[r]]) cnt += 1;
}
while(l <= r && cnt == m){
if(r-l+1
minL = l;
minLength = r-l+1;
}
if(--num[S[l]]
l++;
}
}
return minL == -1 ? "" : S.substr(minL,minLength);
}
};
发表于 2020-12-07 21:23:26
回复(0)
0
1、属于双指针用法中的滑动窗口算法
2、先扩大窗口,找到最大的之后,就要缩短窗口
package main
import (
"fmt"
)
func minWindow(S string, T string) string {
if S == "" || T == "" {
return ""
}
// write code here
winStart, winEnd, count,minLen := 0, 0, len(T),len(S)+1
result := ""
// result := make(map[int]string)
TMap := initTMap(T)
for ; winEnd
_, ok := TMap[string(S[winEnd])]
// 存在
if ok {
TMap[string(S[winEnd])] = TMap[string(S[winEnd])] - 1
// 有可能是重复的字符串
if TMap[string(S[winEnd])] >= 0 {
count--
}
}
// 判断目前的情况是否已经包含所有的字串
// 当已经遍历完之后
if count == 0 {
// 收缩窗口
for winStart <= winEnd{
// fmt.Println(TMap)
_,ok:=TMap[string(S[winStart])]
if ok {
TMap[string(S[winStart])] = TMap[string(S[winStart])] + 1
if TMap[string(S[winStart])] > 0{
// 说明已经找到第一个值
count = len(T)
TMap = initTMap(T)
if minLen > (winEnd-winStart+1){
minLen = winEnd-winStart+1
result = S[winStart : winEnd+1]
}
winStart++
winEnd = winStart
break
}
}
winStart++
}
}
}
return result
}
func initTMap(T string) map[string]int {
TMap := make(map[string]int)
for i := 0; i
// 不直接等于1,因为出现的字符串不一定是只出现一次
TMap[string(T[i])]++
}
return TMap
}
func main() {
fmt.Println(minWindow("XDOYEZODEYXNZ","XYZ"))
}
发表于 2020-09-27 17:30:31
回复(0)
0
这道题目用到了滑动窗口这一大杀器,它可以解决如下问题: 最小覆盖子串(LeetCode76)
字符串排列(LeetCode567)
统计字母异位词(LeetCode438)
最长无重复子串(LeetCode3)
滑动窗口的基本思想: 用两个字典分别维护窗口中字符的统计数量、以及被求解子串中字符的统计数量
用双指针遍历主字符串,双指针的初始值均为0,窗口的范围是[left, right)(左闭右开)
遍历过程中,不断增大、缩小窗口,并更新状态
下面是滑动窗口的基本模版(其中...需要根据题目要求进行修改): void window(string s, string t) {
unordered_map window, target;
for (char c : t) { ++target[c]; }
int left = 0, right = 0; // 初始化双指针
... // 定义状态值
while (right < s.size()) {
// 增大窗口
char c = s[righ]
++right;
... // 更新window
while (达到缩小窗口的条件) {
... // 更新状态值
char c = s[left];
++left;
... // 更新window/状态值
}
}
}
本题代码如下: //
// Created by jt on 2020/9/1.
//
#include
#include
using namespace std;
class Solution {
public:
/**
*
* @param S string字符串
* @param T string字符串
* @return string字符串
*/
string minWindow(string S, string T) {
// write code here
unordered_map window, target;
for (char c : T) ++target[c];
int left = 0, right = 0;
int start = 0, minLen=INT32_MAX;
int count = 0; // 记录窗口中字符是否满足目标
while (right < S.size()) {
char c = S[right];
++right;
if (target.count(c)) {
++window[c];
if (window[c] == target[c]) ++count;
}
while (count == target.size()) {
if (right-left < minLen) {
start = left;
minLen = right-left;
}
c = S[left];
++left;
if (target.count(c)) {
if (window[c] == target[c]) --count;
--window[c];
}
}
}
return minLen==INT32_MAX ? "" : S.substr(start, minLen);
}
};
发表于 2020-09-01 18:21:10
回复(0)
0
#include
#include
class Solution {
public:
string minWindow(string S, string T) {
string res;
if (S.empty() || T.empty()) return res;
unordered_map Map;
unordered_set Set;
for (auto c: T) {
Map[c]--;
Set.insert(c);
}
for (int l = -1, r = -1, len = INT_MAX; r < (int)S.length();)
if (!Set.empty() && ++Map[S[++r]] == 0) Set.erase(S[r]);
else if (Set.empty()) {
if (r - l < len) res = S.substr(l + 1, len = r - l);
if (--Map[S[++l]] == -1) Set.insert(S[l]);
}
return res;
}
};
编辑于 2020-08-18 22:40:50
回复(0)