【问题标题】:How to find smallest substring which contains all characters from a given string?如何找到包含给定字符串中所有字符的最小子字符串?
【发布时间】:2011-01-28 09:57:18
【问题描述】:

我最近遇到了一个关于字符串的有趣问题。假设您得到以下信息:

Input string1: "this is a test string"
Input string2: "tist"
Output string: "t stri"

那么,如上所述,我如何才能找到包含字符串 2 中所有字符的 string1 的最小子字符串?

【问题讨论】:

  • string2 应该是 rist 还是 tisr?在那种情况下,输出不会是“st str”吗?
  • @kennygrimm,string2 以“tist”的形式给出,它应该是。如果您说“rist”或“tisr”而不是您的答案“st str”不包含“i”。
  • 哦,我明白了,我认为 'r' 是错误的,因为它不在 string2 中,但你说它必须包含所有 string2,但也可以包含其他字母......跨度>
  • string2 中的重复项是否也需要计算在内?否则,在string1 中具有tist 的最短子字符串是thisstri

标签: string algorithm substring


【解决方案1】:

要查看包括工作代码在内的更多详细信息,请查看我的博客文章:

http://www.leetcode.com/2010/11/finding-minimum-window-in-s-which.html

为了帮助说明这种方法,我使用了一个示例:string1 = "acbbaca" 和 string2 = "aba"。在这里,我们还使用了术语“窗口”,它表示来自 string1 的连续字符块(可以与术语子字符串互换)。

i) string1 = "acbbaca" 和 string2 = "aba"。

ii) 找到第一个最小窗口。 请注意,我们不能提前开始 指针作为 hasFound['a'] == needToFind['a'] == 2. 推进会 意味着打破约束。

iii) 找到第二个窗口。开始 指针仍然指向第一个 元素'a'。 hasFound['a'] (3) 是 大于 needToFind['a'] (2)。我们 将 hasFound['a'] 减一并且 将开始指针向右推进。

iv) 我们跳过'c',因为它没有找到 在字符串 2 中。开始指针现在指向“b”。 hasFound['b'] (2) 大于 需要查找['b'] (1)。我们递减 hasFound['b'] 减一并提前开始 指针向右。

v) 开始指针现在指向 下一个'b'。 hasFound['b'] (1) 相等 需要ToFind['b'] (1)。我们停下来 马上,这是我们的新 找到最小窗口。

思路主要是在遍历string1时借助两个指针(窗口的开始和结束位置)和两个表(needToFind和hasFound)。 needToFind 将字符的总数存储在 string2 中,hasFound 存储到目前为止遇到的字符的总数。我们还使用 count 变量来存储 string2 中到目前为止遇到的字符总数(不计算 hasFound[x] 超过 needToFind[x] 的字符)。当 count 等于 string2 的长度时,我们知道找到了一个有效的窗口。

每次我们推进结束指针(指向元素 x),我们将 hasFound[x] 加一。如果 hasFound[x] 小于或等于 needToFind[x],我们也将 count 加一。为什么?当满足约束时(即 count 等于 string2 的大小),我们立即将 begin 指针尽可能向右推进,同时保持约束。

我们如何检查它是否保持约束?假设 begin 指向元素 x,我们检查 hasFound[x] 是否大于 needToFind[x]。如果是,我们可以将 hasFound[x] 减一并推进开始指针而不破坏约束。另一方面,如果不是,我们立即停止,因为前进的开始指针打破了窗口约束。

最后,我们检查最小窗口长度是否小于当前最小值。如果找到新的最小值,则更新当前最小值。

本质上,算法找到满足约束的第一个窗口,然后继续保持约束。

【讨论】:

  • 我认为这种方法需要一个更清晰的解释。尤其是像“hasFound”和“needToFind”这样的术语。我很难理解它。
  • needToFind 是从模式字符串 string2 计算的直方图。它是在开始时计算的,并且永远不会改变。在此示例中,needToFind = {'a' : 2, 'b': 1}。另一方面,hasFound 是当前在滑动窗口中的字符的直方图。
【解决方案2】:

您可以在O(N+M) 时间和O(1) 空间中进行直方图扫描,其中N 是第一个字符串中的字符数,M 是第二个字符串中的字符数。

它是这样工作的:

  • 制作第二个字符串的字符直方图(键操作为hist2[ s2[i] ]++)。
  • 制作第一个字符串的字符的累积直方图,直到该直方图包含第二个字符串的直方图包含的每个字符(我将其称为“直方图条件”)。
  • 然后在第一个字符串上向前移动,从直方图中减去,直到它不满足直方图条件。将第一个字符串的那个位(在最后一步之前)标记为您的暂定子字符串。
  • 再次向前移动子字符串的前面,直到再次满足直方图条件。将末端向前移动,直到它再次失败。如果这是一个比第一个更短的子字符串,请将其标记为您的暂定子字符串。
  • 重复直到你通过了整个第一个字符串。
  • 标记的子字符串是您的答案。

请注意,通过改变您在直方图条件上使用的检查,您可以选择将 相同的字符集作为第二个字符串,或者每个字符串至少有同样多的字符输入。 (这只是a[i]>0 && b[i]>0a[i]>=b[i] 之间的区别。)

如果您在尝试满足某个条件时跟踪不满足哪个条件,则可以加快直方图检查,并仅检查您在尝试打破它时减少的内容。 (在初始构建时,您计算您满足的项目数量,并在每次添加将条件从 false 变为 true 的新角色时增加计数。)

【讨论】:

  • +1:这比 python 可读性强得多。如果你能提供一个证明/解释为什么它也能工作,那就太好了。
  • @Rex Kerr:我看不出那是 O(1) 空间。如果所有字符都是唯一的(最坏情况),您的直方图是否不会占用 O(N+M) 空间?
  • O(M) 空间可以完成(而不是 O(N+M)),因为你真的不需要关心不存在的字符是 s2。不过我同意,空间使用量为 O(1) 似乎不正确,并且似乎与描述不符。
  • @Rex:我想我们都知道 O(1) 的含义。你没有抓住重点,证明请求不是关于为什么它是 O(N),而是关于它为什么是正确。如果您愿意,我可以将其添加到您的帖子中。
  • @Moron:你完全正确。我只是按照算法的步骤进行操作,并没有考虑到这个(简单)优化。 @Rex Kerr:从理论上讲,我相信你错了。如果您分配恒定数量的内存,我可以选择足够大的 M 以使您的计数器溢出,因此我们至少需要 O(log_2(M)) 空间。在更实用的符号使用中,我还认为 O(1) 有点误导,因为通常将其与少量固定内存相关联。我们可以满足 O(min(charset size, M)) :)
【解决方案3】:

这是一个 O(n) 的解决方案。基本思想很简单:对于每个起始索引,找到最小的结束索引,使得子字符串包含所有必要的字母。诀窍在于,在函数执行过程中,最小结束索引会增加,因此,只要有一点数据结构支持,我们最多会考虑每个字符两次。

在 Python 中:

from collections import defaultdict

def smallest(s1, s2):
    assert s2 != ''
    d = defaultdict(int)
    nneg = [0]  # number of negative entries in d
    def incr(c):
        d[c] += 1
        if d[c] == 0:
            nneg[0] -= 1
    def decr(c):
        if d[c] == 0:
            nneg[0] += 1
        d[c] -= 1
    for c in s2:
        decr(c)
    minlen = len(s1) + 1
    j = 0
    for i in xrange(len(s1)):
        while nneg[0] > 0:
            if j >= len(s1):
                return minlen
            incr(s1[j])
            j += 1
        minlen = min(minlen, j - i)
        decr(s1[i])
    return minlen

【讨论】:

  • @algorithmist,我没有在 Python 中工作过,但我可以得到那个 for...while 循环;似乎不是 O(n)。您能否以问题中给出的示例为例说明您的方法,不胜感激。
  • j 只能增加 len(s1) 次,所以 while 循环总共做了 O(n) 个工作。
  • @Rajendra:这个算法完全按照我在帖子中描述的那样做,如果有帮助的话——i 标记子字符串的尾部,j 标记头部。 @algorithmist:干得好,提出代码比我提出描述要快!
  • 这不是 O(n) 解决方案!因为在字典中查找本身具有最坏情况复杂度 O(n) en.wikipedia.org/wiki/… 所以你的 n 至少乘以 2
【解决方案4】:

我收到了同样的面试问题。我是一名 C++ 候选人,但我能够在 JAVA 中相对快速地编写代码。

Java [礼貌:Sumod Mathilakath]

import java.io.*;
import  java.util.*;

class UserMainCode
{


    public String GetSubString(String input1,String input2){
        // Write code here...
        return find(input1, input2);
    }
  private static boolean containsPatternChar(int[] sCount, int[] pCount) {
        for(int i=0;i<256;i++) {
            if(pCount[i]>sCount[i])
                return false;
        }
        return true;
    }
  public static String find(String s, String p) {
        if (p.length() > s.length())
            return null;
        int[] pCount = new int[256];
        int[] sCount = new int[256];
        // Time: O(p.lenght)
        for(int i=0;i<p.length();i++) {
            pCount[(int)(p.charAt(i))]++;
            sCount[(int)(s.charAt(i))]++;
        }
        int i = 0, j = p.length(), min = Integer.MAX_VALUE;
        String res = null;
        // Time: O(s.lenght)
        while (j < s.length()) {
            if (containsPatternChar(sCount, pCount)) {
                if ((j - i) < min) {
                    min = j - i;
                    res = s.substring(i, j);
                    // This is the smallest possible substring.
                    if(min==p.length())
                        break;
                    // Reduce the window size.
                    sCount[(int)(s.charAt(i))]--;
                    i++;
                }
            } else {
                sCount[(int)(s.charAt(j))]++;
                // Increase the window size.
                j++;
            }
        }
        System.out.println(res);
        return res;
    }
}

C++ [礼貌:sundeepblue]

#include <iostream>
#include <vector>
#include <string>
#include <climits>
using namespace std;
string find_minimum_window(string s, string t) {
    if(s.empty() || t.empty()) return;

    int ns = s.size(), nt = t.size();
    vector<int> total(256, 0);
    vector<int> sofar(256, 0);
    for(int i=0; i<nt; i++) 
        total[t[i]]++;

    int L = 0, R; 
    int minL = 0;                           //gist2
    int count = 0;
    int min_win_len = INT_MAX;

    for(R=0; R<ns; R++) {                   // gist0, a big for loop
        if(total[s[R]] == 0) continue;
        else sofar[s[R]]++;

        if(sofar[s[R]] <= total[s[R]])      // gist1, <= not <
            count++;

        if(count == nt) {                   // POS1
            while(true) {
                char c = s[L]; 
                if(total[c] == 0) { L++; }
                else if(sofar[c] > total[c]) {
                    sofar[c]--;
                    L++;
                }
                else break;
            }  
            if(R - L + 1 < min_win_len) {   // this judge should be inside POS1
                min_win_len = R - L + 1;
                minL = L;
            }
        }
    }
    string res;
    if(count == nt)                         // gist3, cannot forget this. 
        res = s.substr(minL, min_win_len);  // gist4, start from "minL" not "L"
    return res;
}
int main() {
    string s = "abdccdedca";
    cout << find_minimum_window(s, "acd");
}

Erlang [礼貌:wardbekker]

-module(leetcode).

-export([min_window/0]).

%% 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).

%% For example,
%% S = "ADOBECODEBANC"
%% T = "ABC"
%% Minimum window is "BANC".

%% Note:
%% If there is no such window in S that covers all characters in T, return the emtpy string "".
%% If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.



min_window() ->
    "eca" = min_window("cabeca", "cae"),
    "eca" = min_window("cfabeca", "cae"),
    "aec" = min_window("cabefgecdaecf", "cae"),
    "cwae" = min_window("cabwefgewcwaefcf", "cae"),
    "BANC" = min_window("ADOBECODEBANC", "ABC"),
    ok.

min_window(T, S) ->
    min_window(T, S, []).

min_window([], _T, MinWindow) ->
    MinWindow;
min_window([H | Rest], T, MinWindow) ->
    NewMinWindow = case lists:member(H, T) of
                       true ->
                           MinWindowFound = fullfill_window(Rest, lists:delete(H, T), [H]),
                           case length(MinWindow) == 0 orelse (length(MinWindow) > length(MinWindowFound)
                               andalso length(MinWindowFound) > 0) of
                               true ->
                                   MinWindowFound;
                               false ->
                                   MinWindow
                           end;
                       false ->
                           MinWindow
                   end,
    min_window(Rest, T, NewMinWindow).

fullfill_window(_, [], Acc) ->
    %% window completed
    Acc;
fullfill_window([], _T, _Acc) ->
    %% no window found
    "";
fullfill_window([H | Rest], T, Acc) ->
    %% completing window
    case lists:member(H, T) of
        true ->
            fullfill_window(Rest, lists:delete(H, T), Acc ++ [H]);
        false ->
            fullfill_window(Rest, T, Acc ++ [H])
    end.

参考:

【讨论】:

    【解决方案5】:

    请也看看这个:

    //-----------------------------------------------------------------------
    
    bool IsInSet(char ch, char* cSet)
    {
        char* cSetptr = cSet;
        int index = 0;
        while (*(cSet+ index) != '\0')
        {
            if(ch == *(cSet+ index))
            {
                return true;            
            }
            ++index;
        }
        return false;
    }
    
    void removeChar(char ch, char* cSet)
    {
        bool bShift = false;
        int index = 0;
        while (*(cSet + index) != '\0')
        {
            if( (ch == *(cSet + index)) || bShift)
            {
                *(cSet + index) = *(cSet + index + 1);
                bShift = true;
            }
            ++index;
        }
    }
    typedef struct subStr
    {
        short iStart;
        short iEnd;
        short szStr;
    }ss;
    
    char* subStringSmallest(char* testStr, char* cSet)
    {
        char* subString = NULL;
        int iSzSet = strlen(cSet) + 1;
        int iSzString = strlen(testStr)+ 1;
        char* cSetBackUp = new char[iSzSet];
        memcpy((void*)cSetBackUp, (void*)cSet, iSzSet);
    
        int iStartIndx = -1;    
        int iEndIndx = -1;
        int iIndexStartNext = -1;
    
        std::vector<ss> subStrVec;
        int index = 0;
    
        while( *(testStr+index) != '\0' )
        {
            if (IsInSet(*(testStr+index), cSetBackUp))
            {
                removeChar(*(testStr+index), cSetBackUp);
    
                if(iStartIndx < 0)
                {
                    iStartIndx = index;
                }
                else if( iIndexStartNext < 0)
                    iIndexStartNext = index;
                else
                    ;
    
                if (strlen(cSetBackUp) == 0 )
                {
                    iEndIndx = index;
                    if( iIndexStartNext == -1)
                        break;
                    else
                    {
                        index = iIndexStartNext;
                        ss stemp = {iStartIndx, iEndIndx, (iEndIndx-iStartIndx + 1)};
                        subStrVec.push_back(stemp);
                        iStartIndx = iEndIndx = iIndexStartNext = -1;
                        memcpy((void*)cSetBackUp, (void*)cSet, iSzSet);
                        continue;
                    }
                }
            }
            else
            {
                if (IsInSet(*(testStr+index), cSet))
                {
                    if(iIndexStartNext < 0)
                        iIndexStartNext = index;
                }
            }
    
            ++index;
        }
    
    
        int indexSmallest = 0;
        for(int indexVec = 0; indexVec < subStrVec.size(); ++indexVec)
        {
            if(subStrVec[indexSmallest].szStr > subStrVec[indexVec].szStr)
                indexSmallest = indexVec;       
        }
    
        subString = new char[(subStrVec[indexSmallest].szStr) + 1];
        memcpy((void*)subString, (void*)(testStr+ subStrVec[indexSmallest].iStart), subStrVec[indexSmallest].szStr);
        memset((void*)(subString + subStrVec[indexSmallest].szStr), 0, 1);
    
        delete[] cSetBackUp;
        return subString;
    }
    //--------------------------------------------------------------------
    

    【讨论】:

      【解决方案6】:

      编辑:显然有一个 O(n) 算法(参见算法专家的回答)。显然,这将击败下面描述的 [naive] 基线!

      太糟糕了,我得走了……我有点怀疑我们能得到 O(n)。明天我会签到看看获胜者;-) 玩得开心!

      暂定算法
      总体思路是依次尝试并使用在 str1 中找到的 str2 中的一个字符作为对 str2 的所有其他字母的搜索(在一个/两个方向上)的开始。通过保持“迄今为止的最佳匹配长度”值,我们可以在搜索超出此值时中止搜索。其他启发式可能可用于进一步中止次优(到目前为止)解决方案。 str1 中起始字母顺序的选择很重要。建议从 str1 中计数最少的字母开始,在后续尝试中尝试使用其他计数增加的字母。

        [loose pseudo-code]
        - get count for each letter/character in str1  (number of As, Bs etc.)
        - get count for each letter in str2
        - minLen = length(str1) + 1  (the +1 indicates you're not sure all chars of 
                                      str2 are in str1)
        - Starting with the letter from string2 which is found the least in string1,
          look for other letters of Str2, in either direction of str1, until you've 
          found them all (or not, at which case response = impossible => done!). 
          set x = length(corresponding substring of str1).
       - if (x < minLen), 
               set minlen = x, 
               also memorize the start/len of the str1 substring.
       - continue trying with other letters of str1 (going the up the frequency
         list in str1), but abort search as soon as length(substring of strl) 
         reaches or exceed minLen.  
         We can find a few other heuristics that would allow aborting a 
         particular search, based on [pre-calculated ?] distance between a given
         letter in str1 and some (all?) of the letters in str2.
       - the overall search terminates when minLen = length(str2) or when 
         we've used all letters of str1 (which match one letter of str2)
         as a starting point for the search
      

      【讨论】:

        【解决方案7】:

        这里是Java实现

        public static String shortestSubstrContainingAllChars(String input, String target) {
            int needToFind[] = new int[256];
            int hasFound[] = new int[256];
            int totalCharCount = 0;
            String result = null;
        
            char[] targetCharArray = target.toCharArray();
            for (int i = 0; i < targetCharArray.length; i++) {
                needToFind[targetCharArray[i]]++;           
            }
        
            char[] inputCharArray = input.toCharArray();
            for (int begin = 0, end = 0; end < inputCharArray.length; end++) {
        
                if (needToFind[inputCharArray[end]] == 0) {
                    continue;
                }
        
                hasFound[inputCharArray[end]]++;
                if (hasFound[inputCharArray[end]] <= needToFind[inputCharArray[end]]) {
                    totalCharCount ++;
                }
                if (totalCharCount == target.length()) {
                    while (needToFind[inputCharArray[begin]] == 0 
                            || hasFound[inputCharArray[begin]] > needToFind[inputCharArray[begin]]) {
        
                        if (hasFound[inputCharArray[begin]] > needToFind[inputCharArray[begin]]) {
                            hasFound[inputCharArray[begin]]--;
                        }
                        begin++;
                    }
        
                    String substring = input.substring(begin, end + 1);
                    if (result == null || result.length() > substring.length()) {
                        result = substring;
                    }
                }
            }
            return result;
        }
        

        这里是 Junit 测试

        @Test
        public void shortestSubstringContainingAllCharsTest() {
            String result = StringUtil.shortestSubstrContainingAllChars("acbbaca", "aba");
            assertThat(result, equalTo("baca"));
        
            result = StringUtil.shortestSubstrContainingAllChars("acbbADOBECODEBANCaca", "ABC");
            assertThat(result, equalTo("BANC"));
        
            result = StringUtil.shortestSubstrContainingAllChars("this is a test string", "tist");
            assertThat(result, equalTo("t stri"));
        }
        

        【讨论】:

          【解决方案8】:
          //[ShortestSubstring.java][1]
          
          public class ShortestSubstring {
          
              public static void main(String[] args) {
                  String input1 = "My name is Fran";
                  String input2 = "rim";
                  System.out.println(getShortestSubstring(input1, input2));
              }
          
              private static String getShortestSubstring(String mainString, String toBeSearched) {
          
                  int mainStringLength = mainString.length();
                  int toBeSearchedLength = toBeSearched.length();
          
                  if (toBeSearchedLength > mainStringLength) {
                      throw new IllegalArgumentException("search string cannot be larger than main string");
                  }
          
                  for (int j = 0; j < mainStringLength; j++) {
                      for (int i = 0; i <= mainStringLength - toBeSearchedLength; i++) {
                          String substring = mainString.substring(i, i + toBeSearchedLength);
                          if (checkIfMatchFound(substring, toBeSearched)) {
                              return substring;
                          }
                      }
                      toBeSearchedLength++;
                  }
          
                  return null;
              }
          
              private static boolean checkIfMatchFound(String substring, String toBeSearched) {
                  char[] charArraySubstring = substring.toCharArray();
                  char[] charArrayToBeSearched = toBeSearched.toCharArray();
                  int count = 0;
          
                  for (int i = 0; i < charArraySubstring.length; i++) {
                      for (int j = 0; j < charArrayToBeSearched.length; j++) {
                          if (String.valueOf(charArraySubstring[i]).equalsIgnoreCase(String.valueOf(charArrayToBeSearched[j]))) {
                              count++;
                          }
                      }
                  }
                  return count == charArrayToBeSearched.length;
              }
          }
          

          【讨论】:

          • 虽然此代码可能有助于解决问题,但提供有关 why 和/或 如何 回答问题的额外上下文将显着改善其长期长期价值。请edit你的答案添加一些解释。
          【解决方案9】:

          这是一种使用素数来避免循环的方法,并将其替换为乘法。可以进行其他一些小的优化。

          1. 为您要查找的任何字符分配一个唯一的素数,并为不感兴趣的字符分配一个1

          2. 通过将质数乘以它应该出现的次数来找到匹配字符串的乘积。现在只有使用相同的素数才能找到这个产品。

          3. 从头开始搜索字符串,当您移动到正在运行的产品时,将相应的质数相乘。

          4. 如果数字大于正确的总和,请删除第一个字符并将其质数从正在运行的产品中除以。

          5. 如果数字小于正确的总和,则包含下一个字符并将其乘以您的运行乘积。

          6. 如果数字与找到匹配项的正确总和相同,则将开头和结尾滑动到下一个字符并继续搜索其他匹配项。

          7. 决定哪一个匹配最短。

          Gist

          charcount = { 'a': 3, 'b' : 1 };
          str = "kjhdfsbabasdadaaaaasdkaaajbajerhhayeom"
          
          def find (c, s):
            Ns = len (s)
          
            C = list (c.keys ())
            D = list (c.values ())
          
            # prime numbers assigned to the first 25 chars
            prmsi = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89 , 97]
          
            # primes used in the key, all other set to 1
            prms = []
            Cord = [ord(c) - ord('a') for c in C]
          
            for e,p in enumerate(prmsi):
              if e in Cord:
                prms.append (p)
              else:
                prms.append (1)
          
            # Product of match
            T = 1
            for c,d in zip(C,D):
              p = prms[ord (c) - ord('a')]
              T *= p**d
          
            print ("T=", T)
          
            t = 1 # product of current string
            f = 0
            i = 0
          
            matches = []
            mi = 0
            mn = Ns
            mm = 0
          
            while i < Ns:
              k = prms[ord(s[i]) - ord ('a')]
              t *= k
          
              print ("testing:", s[f:i+1])
          
              if (t > T):
                # included too many chars: move start
                t /= prms[ord(s[f]) - ord('a')] # remove first char, usually division by 1
                f += 1 # increment start position
                t /= k # will be retested, could be replaced with bool
          
              elif t == T:
                # found match
                print ("FOUND match:", s[f:i+1])
                matches.append (s[f:i+1])
          
                if (i - f) < mn:
                  mm = mi
                  mn = i - f
          
                mi += 1
          
                t /= prms[ord(s[f]) - ord('a')] # remove first matching char
          
                # look for next match
                i += 1
                f += 1
          
              else:
                # no match yet, keep searching
                i += 1
          
            return (mm, matches)
          
          
          print (find (charcount, str))
          

          (注意:这个答案最初是针对一个重复的问题发布的,原来的答案现在已被删除。)

          【讨论】:

            【解决方案10】:

            C# 实现:

            public static Tuple<int, int> FindMinSubstringWindow(string input, string pattern)
            {
                Tuple<int, int> windowCoords = new Tuple<int, int>(0, input.Length - 1);
                int[] patternHist = new int[256];
                for (int i = 0; i < pattern.Length; i++)
                {
                    patternHist[pattern[i]]++;
                }
                int[] inputHist = new int[256];
                int minWindowLength = int.MaxValue;
                int count = 0;
                for (int begin = 0, end = 0; end < input.Length; end++)
                {
                    // Skip what's not in pattern.
                    if (patternHist[input[end]] == 0)
                    {
                        continue;
                    }
                    inputHist[input[end]]++;
                    // Count letters that are in pattern.
                    if (inputHist[input[end]] <= patternHist[input[end]])
                    {
                        count++;
                    }
                    // Window found.
                    if (count == pattern.Length)
                    {
                        // Remove extra instances of letters from pattern
                        // or just letters that aren't part of the pattern
                        // from the beginning.
                        while (patternHist[input[begin]] == 0 ||
                               inputHist[input[begin]] > patternHist[input[begin]])
                        {
                            if (inputHist[input[begin]] > patternHist[input[begin]])
                            {
                                inputHist[input[begin]]--;
                            }
                            begin++;
                        }
                        // Current window found.
                        int windowLength = end - begin + 1;
                        if (windowLength < minWindowLength)
                        {
                            windowCoords = new Tuple<int, int>(begin, end);
                            minWindowLength = windowLength;
                        }
                    }
                }
                if (count == pattern.Length)
                {
                    return windowCoords;
                }
                return null;
            }
            

            【讨论】:

              【解决方案11】:

              我已经使用 Python3 以 O(N) 的效率实现了它:

              def get(s, alphabet="abc"):
                  seen = {}
                  for c in alphabet:
                      seen[c] = 0
                  seen[s[0]] = 1
                  start = 0
                  end = 0
                  shortest_s = 0
                  shortest_e = 99999
                  while end + 1 < len(s):
                      while seen[s[start]] > 1:
                          seen[s[start]] -= 1
                          start += 1
                      # Constant time check:
                      if sum(seen.values()) == len(alphabet) and all(v == 1 for v in seen.values()) and \
                              shortest_e - shortest_s > end - start:
                          shortest_s = start
                          shortest_e = end
                      end += 1
                      seen[s[end]] += 1
                  return s[shortest_s: shortest_e + 1]
              
              
              print(get("abbcac")) # Expected to return "bca"
              

              【讨论】:

                【解决方案12】:
                    String s = "xyyzyzyx";
                    String s1 = "xyz";
                    String finalString ="";
                    Map<Character,Integer> hm = new HashMap<>();
                    if(s1!=null && s!=null && s.length()>s1.length()){
                        for(int i =0;i<s1.length();i++){
                            if(hm.get(s1.charAt(i))!=null){
                                int k = hm.get(s1.charAt(i))+1;
                                hm.put(s1.charAt(i), k);
                            }else
                                hm.put(s1.charAt(i), 1);
                        }
                        Map<Character,Integer> t = new HashMap<>();
                        int start =-1;
                         for(int j=0;j<s.length();j++){
                             if(hm.get(s.charAt(j))!=null){
                                 if(t.get(s.charAt(j))!=null){
                                     if(t.get(s.charAt(j))!=hm.get(s.charAt(j))){
                                     int k = t.get(s.charAt(j))+1;
                                        t.put(s.charAt(j), k);
                                     }
                                 }else{
                                     t.put(s.charAt(j), 1);
                                     if(start==-1){
                                         if(j+s1.length()>s.length()){
                                             break;
                                         }
                                         start = j;
                                     }
                                 }
                                 if(hm.equals(t)){
                                    t = new HashMap<>();
                                    if(finalString.length()<s.substring(start,j+1).length());
                                    {
                                        finalString=s.substring(start,j+1);
                                    }
                                    j=start;
                                    start=-1;                       
                                 }
                             }
                         }
                

                【讨论】:

                • 您能否解释一下为什么如何您的代码 sn-p 提供了问题的答案?谢谢。
                • 我正在使用两个 HashMaps 来存储每个字符串中的字符数并检查两个映射是否相等,如果两个映射相等,那么我们就得到了给定字符串中的子字符串。
                【解决方案13】:

                暴力破解JavaScript解决方案:

                function shortestSubStringOfUniqueChars(s){
                	var uniqueArr = [];
                	for(let i=0; i<s.length; i++){
                		if(uniqueArr.indexOf(s.charAt(i)) <0){
                			uniqueArr.push(s.charAt(i));
                		}
                	}
                
                	let windoww = uniqueArr.length;
                
                	while(windoww < s.length){
                		for(let i=0; i<s.length - windoww; i++){
                			let match = true;
                			let tempArr = [];
                			for(let j=0; j<uniqueArr.length; j++){
                				if(uniqueArr.indexOf(s.charAt(i+j))<0){
                					match = false;
                					break;
                				}
                			}
                		let checkStr
                		if(match){
                			checkStr =  s.substr(i, windoww);
                			for(let j=0; j<uniqueArr.length; j++){
                				if(uniqueArr.indexOf(checkStr.charAt(j))<0){
                					match = false;
                					break;
                				}
                			}
                		}
                		if(match){
                		    return checkStr;
                		}
                 	 }
                 	 windoww = windoww + 1;
                	}
                }
                
                console.log(shortestSubStringOfUniqueChars("ABA"));

                【讨论】:

                  【解决方案14】:

                  上述方法的 Java 代码:

                  private static Map<Character, Integer> frequency;
                  private static Set<Character> charsCovered;
                  private static Map<Character, Integer> encountered;
                  /**
                   * To set the first match index as an intial start point
                   */
                  private static boolean hasStarted = false;
                  private static int currentStartIndex = 0;
                  private static int finalStartIndex = 0;
                  private static int finalEndIndex = 0;
                  private static int minLen = Integer.MAX_VALUE;
                  private static int currentLen = 0;
                  /**
                   * Whether we have already found the match and now looking for other
                   * alternatives.
                   */
                  private static boolean isFound = false;
                  private static char currentChar;
                  
                  public static String findSmallestSubStringWithAllChars(String big, String small) {
                  
                      if (null == big || null == small || big.isEmpty() || small.isEmpty()) {
                          return null;
                      }
                  
                      frequency = new HashMap<Character, Integer>();
                      instantiateFrequencyMap(small);
                      charsCovered = new HashSet<Character>();
                      int charsToBeCovered = frequency.size();
                      encountered = new HashMap<Character, Integer>();
                  
                      for (int i = 0; i < big.length(); i++) {
                          currentChar = big.charAt(i);
                          if (frequency.containsKey(currentChar) && !isFound) {
                              if (!hasStarted && !isFound) {
                                  hasStarted = true;
                                  currentStartIndex = i;
                              }
                              updateEncounteredMapAndCharsCoveredSet(currentChar);
                              if (charsCovered.size() == charsToBeCovered) {
                                  currentLen = i - currentStartIndex;
                                  isFound = true;
                                  updateMinLength(i);
                              }
                          } else if (frequency.containsKey(currentChar) && isFound) {
                              updateEncounteredMapAndCharsCoveredSet(currentChar);
                              if (currentChar == big.charAt(currentStartIndex)) {
                                  encountered.put(currentChar, encountered.get(currentChar) - 1);
                                  currentStartIndex++;
                                  while (currentStartIndex < i) {
                                      if (encountered.containsKey(big.charAt(currentStartIndex))
                                              && encountered.get(big.charAt(currentStartIndex)) > frequency.get(big
                                                      .charAt(currentStartIndex))) {
                                          encountered.put(big.charAt(currentStartIndex),
                                                  encountered.get(big.charAt(currentStartIndex)) - 1);
                                      } else if (encountered.containsKey(big.charAt(currentStartIndex))) {
                                          break;
                                      }
                                      currentStartIndex++;
                                  }
                              }
                              currentLen = i - currentStartIndex;
                              updateMinLength(i);
                          }
                      }
                      System.out.println("start: " + finalStartIndex + " finalEnd : " + finalEndIndex);
                      return big.substring(finalStartIndex, finalEndIndex + 1);
                  }
                  
                  private static void updateMinLength(int index) {
                      if (minLen > currentLen) {
                          minLen = currentLen;
                          finalStartIndex = currentStartIndex;
                          finalEndIndex = index;
                      }
                  
                  }
                  
                  private static void updateEncounteredMapAndCharsCoveredSet(Character currentChar) {
                      if (encountered.containsKey(currentChar)) {
                          encountered.put(currentChar, encountered.get(currentChar) + 1);
                      } else {
                          encountered.put(currentChar, 1);
                      }
                  
                      if (encountered.get(currentChar) >= frequency.get(currentChar)) {
                          charsCovered.add(currentChar);
                      }
                  }
                  
                  private static void instantiateFrequencyMap(String str) {
                  
                      for (char c : str.toCharArray()) {
                          if (frequency.containsKey(c)) {
                              frequency.put(c, frequency.get(c) + 1);
                          } else {
                              frequency.put(c, 1);
                          }
                      }
                  
                  }
                  
                  public static void main(String[] args) {
                  
                      String big = "this is a test string";
                      String small = "tist";
                      System.out.println("len: " + big.length());
                      System.out.println(findSmallestSubStringWithAllChars(big, small));
                  }
                  

                  【讨论】:

                    【解决方案15】:
                    def minimum_window(s, t, min_length = 100000):
                        d = {}
                        for x in t:
                            if x in d:
                                d[x]+= 1
                            else:
                                d[x] = 1
                    
                        tot = sum([y for x,y in d.iteritems()])
                        l = []
                        ind = 0 
                        for i,x in enumerate(s):
                            if ind == 1:
                                l = l + [x]
                            if x in d:
                                tot-=1
                                if not l:
                                    ind = 1
                                    l = [x]
                    
                            if tot == 0:
                                if len(l)<min_length:
                                    min_length = len(l)
                                    min_length = minimum_window(s[i+1:], t, min_length)
                    
                    return min_length
                    
                    l_s = "ADOBECODEBANC"
                    t_s = "ABC"
                    
                    min_length = minimum_window(l_s, t_s)
                    
                    if min_length == 100000:
                          print "Not found"
                    else:
                          print min_length
                    

                    【讨论】:

                      猜你喜欢
                      • 2015-02-18
                      • 1970-01-01
                      • 2016-03-26
                      • 2012-05-04
                      • 2021-06-11
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多