这道题,刚开始拿到的时候,自己是没有思路的,然后自己尝试着去写,错的多了,也就有思路了,但是超时了,于是看看别人使用的什么办法。嘿嘿
题目:
给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC" 输出: "BANC"
说明:
- 如果 S 中不存这样的子串,则返回空字符串
""
。 - 如果 S 中存在这样的子串,我们保证它是唯一的答案。
首先看下我的超时代码,但是正确的。
package test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LC76Try1
{
public String minWindow(String s, String t)
{
String ret = "";
int ls = s.length();
int lt = t.length();
if (ls < lt)
{
return "";
}
Map<String, Integer> map = new HashMap<String, Integer>();
Map<String,List<Integer>> dp = new HashMap<String, List<Integer>>();
int[] ans=new int[ls];
//查看各个元素出现的次数
for(int i=0;i<lt;i++){
String st=String.valueOf(t.charAt(i));
if(map.containsKey(st)){
Integer count=map.get(st);
map.put(st, count+1);
}else{
map.put(st, 1);
}
}
//初始dp,dp【key,list】,其中s中和key相同的下坐标
for(int j=0;j<lt;j++){
String s1=String.valueOf(t.charAt(j));
char c1=t.charAt(j);
if(dp.containsKey(s1)){
continue;
}else{
List<Integer> list =new ArrayList<Integer>();
for(int k=0;k<ls;k++){
char c2=s.charAt(k);
if(c1==c2){
list.add(k);
}
}
if(list.size()==0){
return "";
}else{
dp.put(s1, list);
}
}
}
//ans【i】记录的就是从i开始的最小窗口
for(int i=0;i<ls;i++){
String s2=String.valueOf(s.charAt(i));
if(map.containsKey(s2)){
int max=-1;
for(String s3 :map.keySet()){
Integer count = map.get(s3);
List<Integer> l2=dp.get(s3);
int temp=0;
for(int j=0;j<l2.size();j++){
if(l2.get(j)>=i){
temp++;
if(temp==count){
if(l2.get(j)>max){
max=l2.get(j);
}
break;
}
}
}
if(temp<count){
max=-1;
break;
}
}
ans[i]=max;
}else{
ans[i]=-1;
continue;
}
}
int x=0;
int y=-1;
int min=ls;
//遍历所有的窗口,找到最小的
for(int i=0;i<ls;i++){
if(ans[i]==-1){
continue;
}else{
if(ans[i]-i<min){
min=ans[i]-i;
x=i;
y=ans[i];
}
}
}
ret=s.substring(x,y+1);
return ret;
}
public static void main(String[] args)
{
LC76Try1 t = new LC76Try1();
String ret = t.minWindow("a", "");
System.out.println(ret);
}
}
然后,我就看了这位的博客,我觉得人家讲的蛮好的。很容易理解。https://blog.csdn.net/u013115610/article/details/70257445,防止他的丢了,我用的人家的图
思路:采用滑动窗口,窗口有左右边界,先通过扩展右边界找出一个包含T中所有字符的子串,然后收缩左边界,直到不能再收缩。记录此时的子串。然后收缩左边界,继续扩展右边界,直到再找到满足要求的子串,和上次的进行比较,保存更小的子串。返回执行,直到右边界到达S串尾,且左边界不能再收缩。
代码:还是我自己写吧:貌似时间还是比较慢:
package test;
import java.util.HashMap;
import java.util.Map;
public class LC76Try2
{
public String minWindow(String s, String t)
{
String ret = "";
int ls = s.length();
int lt = t.length();
if (ls < lt)
{
return "";
}
Map<Character, Integer> map = new HashMap<Character, Integer>();
for (int i = 0; i < lt; i++)
{
char c1 = t.charAt(i);
if (map.containsKey(c1))
{
Integer count = map.get(c1);
map.put(c1, count + 1);
}
else
{
map.put(c1, 1);
}
}
int left = 0;
int cn = 0;
int x = 0;
int y = -1;
int min = ls;
for (int right = 0; right < ls; right++)
{
char c2 = s.charAt(right);
if (map.containsKey(c2))
{
Integer tn = map.get(c2);
tn--;
if (tn >= 0)
{
cn++;
}
map.put(c2, tn);
}
while (cn == lt)
{
if (right - left < min)
{
min = right - left;
x = left;
y = right;
}
c2 = s.charAt(left);
if (map.containsKey(c2))
{
if (map.get(c2) >= 0)
{
cn--;
}
Integer tn = map.get(c2);
tn++;
map.put(c2, tn);
}
left++;
}
}
ret=s.substring(x,y+1);
return ret;
}
public static void main(String[] args)
{
LC76Try2 t = new LC76Try2();
String ret = t.minWindow("a", "a");
System.out.println(ret);
}
}
更快的,不用map的,代码:
package test;
public class LC76Try3
{
public String minWindow(String s, String t)
{
int start = 0, minLen = Integer.MAX_VALUE;
int left = 0;
int[] map = new int[256];
int count = t.length();
for (char c : t.toCharArray())
map[c]++;
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if (map[c]-- > 0)
count--;
while (count == 0)
{
if (i - left + 1 < minLen)
{
minLen = i - left + 1;
start = left;
}
if (map[s.charAt(left)]++ == 0)
count++;
left++;
}
}
return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start
+ minLen);
}
}
嘿嘿