java最小子串覆盖_最小覆盖子串

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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值