最小子串覆盖

来源:互联网 发布:淘宝网天猫食品 编辑:程序博客网 时间:2024/05/28 11:49

给定一个字符串source和一个目标字符串target,在字符串source找到包括所有目标字符串字母的子串。

 注意事项

如果在source没有这样的子串,返回"",如果有多个这样的子串,返回起始位置最小的子串。

说明

在答案的子串中的字母在目标字符串中是否需要具有相同的顺序?

——不需要。

样例

给出source = "ADOBECODEBANC"target = "ABC" 满足要求的解  "BANC"

1:暴力方法

import java.util.HashMap;import java.util.Map;import java.util.Scanner;/** * 给定一个字符串source和一个目标字符串target,在字符串source中找到包括所有目标字符串字母的子串。 注意事项如果在source中没有这样的子串,返回"",如果有多个这样的子串,返回起始位置最小的子串。您在真实的面试中是否遇到过这个题? Yes说明在答案的子串中的字母在目标字符串中是否需要具有相同的顺序?——不需要。样例给出source = "ADOBECODEBANC",target = "ABC" 满足要求的解  "BANC" *  * @author Dell * */public class Test32 {public static String minWindow(String source,String target){       String result="";   for(int i=0;i<source.length();i++)   {  for(int j=i;j<source.length();j++)   {  String temp=source.substring(i, j+1);  if(temp.length()<target.length())  continue;  int k;  boolean[] visited=new boolean[temp.length()];         for(k=0;k<target.length();k++)         {     boolean flag=false;        for(int m=0;m<temp.length();m++)        {        if(target.charAt(k)==temp.charAt(m)&&visited[m]==false)        {        visited[m]=true;        flag=true;        break;        }        else        continue;        }         if(flag==false)         break;         }                  if(k>=target.length())         {         if(result==""||result.length()>temp.length())         {         result=temp;         }         }   }   }return result;}public static void main(String[] args) {   Scanner sc=new Scanner(System.in);   String source=sc.nextLine();   String target=sc.nextLine();  System.out.println(minWindow(source,target));     }}

2:

给定一个字符串S和T,在S中找到一个最小的子串包含T中的所有字符,时间复杂度为O(n)。
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

举例,
S = "ADOBECODEBANC"
T = "ABC"
最小子串是"BANC".

时间复杂度是O(n)

其中n是字符串S的长度,因为每个字符在维护窗口的过程中不会被访问多于两次。

空间复杂度则是O(1)

也就是代码中T的长度。

数据结构选择

记录字母出现次数的数据结构有采用哈希Map的也有采用数组的,这里介绍一下数组的用法,因为它用了比较取巧的方式。
大小写字母的ASCII码不大于128,这样array['A']=3指A出现3次,array['B']=1指B出现了一次,以此类推,不能用常规意义上的定义array[0]=3表示A出现3次,这样就多了一层映射!故而数组的长度设置为128即可存放所有的字母。明白了么?

算法思想

  1. 首先预处理T,用一个128长 (示例代码用了256) 的整数数组srcHash存储里面每个目标字符出现的个数
  2. 然后处理原串S,也用一个128长的整数数组destHash记录原串字符出现的个数。给定两个指针start和end,作为最小窗口的左右边界。具体实现看下代码一目了然。
  public class Solution {        public String minWindow(String S, String T) {            int[] srcHash = new int[255];            // 记录目标字符串每个字母出现次数            for(int i = 0; i < T.length(); i++){                srcHash[T.charAt(i)]++;            }            int start = 0,i= 0;            // 用于记录窗口内每个字母出现次数             int[] destHash = new int[255];            int found = 0;            int begin = -1, end = S.length(), minLength = S.length();            for(start = i = 0; i < S.length(); i++){                // 每来一个字符给它的出现次数加1                destHash[S.charAt(i)]++;                // 如果加1后这个字符的数量不超过目标串中该字符的数量,则找到了一个匹配字符                if(destHash[S.charAt(i)] <= srcHash[S.charAt(i)]) found++;                // 如果找到的匹配字符数等于目标串长度,说明找到了一个符合要求的子串                    if(found == T.length()){                    // 将开头没用的都跳过,没用是指该字符出现次数超过了目标串中出现的次数,并把它们出现次数都减1                    while(start < i && destHash[S.charAt(start)] > srcHash[S.charAt(start)]){                        destHash[S.charAt(start)]--;                        start++;                    }                    // 这时候start指向该子串开头的字母,判断该子串长度                    if(i - start < minLength){                        minLength = i - start;                        begin = start;                        end = i;                    }                    // 把开头的这个匹配字符跳过,并将匹配字符数减1                    destHash[S.charAt(start)]--;                    found--;                    // 子串起始位置加1,我们开始看下一个子串了                    start++;                }            }            // 如果begin没有修改过,返回空            return begin == -1 ? "" : S.substring(begin,end + 1);        }    }







原创粉丝点击