最小子串覆盖

给定一个字符串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);
        }
    }







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值