字符串x中的最小窗口宽度,包含字符串y的所有字符

时间:2010-08-28 19:10:41

标签: algorithm

在字符串x中查找包含另一个字符串y的所有字符的最小窗口宽度。例如:

String x = "coobdafceeaxab"
String y = "abc"

答案应该是5,因为x中包含y所有三个字母的最短子字符串是“bdafc”。

我可以想到一个复杂O(n^2 * log(m))的天真解决方案,其中n = len(x)m = len(y)。有谁能建议更好的解决方案?感谢。

更新:现在想起来,如果我将设置更改为tr1::unordered_map,那么我可以将复杂度降低到O(n^2),因为插入和删除都应该是O(1)

7 个答案:

答案 0 :(得分:21)

时间:O(n)(一次通过)
空间:O(k)

我就是这样做的:
为字符串Y中的所有字符创建哈希表。(我假设Y中的所有字符都不同)。

第一遍:
从字符串X的第一个字符开始 更新哈希表,用于exa:用于键'a'输入位置(比如1) 继续这样做,直到你从Y获得所有字符(直到哈希表中的所有键都有值) 如果再次获得某个角色,请更新其较新的值并删除较旧的角色。

首次通过后,从哈希表和最大值中取最小值 这是迄今为止观察到的最小窗口。

现在,转到字符串X中的下一个字符,更新哈希表,看看你是否得到更小的窗口。

<小时/> 修改:

让我们举一个例子:
字符串x =“coobdafceeaxab”
字符串y =“abc”

首先从Y的字符初始化哈希表 h [a] = -1
h [b] = -1
h [c] = -1

现在,从X的第一个角色开始 第一个字符是c,h [c] = 0
第二个字符(o)不是哈希的一部分,跳过它。
..
第四个字符(b),h [b] = 3
..
第六个字符(a),输入哈希表h [a] = 5.
现在,哈希表中的所有键都有一些价值 最小值为0(c),最高值为5(a),最小窗口为6(0到5)。
第一轮完成。

采取下一个角色。 f不是哈希表的一部分,跳过它。
下一个字符(c),更新哈希表h [c] = 7 找到新窗口,最小值为3(b),最高值为7(c) 新窗口是3到7 =&gt; 5。

继续这样做直到字符串X的最后一个字符。

我希望现在明白。

<小时/> 的修改

有一些关于从哈希中查找最大值和最小值的担忧 我们可以维护已排序的链接列表并将其映射到哈希表 每当链接列表中的任何元素发生更改时,都应将其重新映射到哈希表 这两个操作都是O(1)

总空间为m + m

<小时/> 的修改

以上是上述问题的小型可视化: 对于“coobdafceeaxab”和“abc”

步骤-0:
初始双重链表= NULL
初始hash-table = NULL

步骤1:
头&LT; - &GT;并[c,0]&LT; - &GT;尾
h [c] = [0,'指向LL中c节点的指针']

步骤-2:
头&LT; - &GT;并[c,0]&LT; - &GT;并[b,3]&LT; - &GT;尾
h [c] = [0,'指向LL中c节点的指针'],h [b] = [3,'指向LL中b节点的指针'],

步骤-3:
头&LT; - &GT;并[c,0]&LT; - &GT;并[b,3]&LT; - &GT; [α,5]&LT; - &GT;尾
h [c] = [0,'指向LL中c节点的指针'],h [b] = [3,'指向LL中b节点的指针'],h [a] = [5,'指向节点的指针LL']
最小窗口=&gt;与尾巴和头部的差异=&gt; (5-0)+1 =&gt;长度:6

步骤4:
在此更新C到索引7的条目。 (从链表中删除'c'节点并附加在尾部)
头&LT; - &GT;并[b,3]&LT; - &GT; [α,5]&LT; - &GT;并[c,7]&LT; - &GT;尾
h [c] = [7,'指向LL中的c节点的新指针'],h [b] = [3,'指向LL中b节点的指针'],h [a] = [5,'指向节点的指针在LL'],
最小窗口=&gt;与尾巴和头部的差异=&gt; (7-3)+1 =&gt;长度:5

等等..

请注意,上面的链表更新和哈希表更新都是O(1) 如果我错了,请纠正我。

<小时/> 的总结:

时间复杂度:O(n)一次通过
空间复杂度:O(k)其中k是字符串Y的长度

答案 1 :(得分:5)

我在这里找到了这个非常好的O(N)时间复杂度版本http://leetcode.com/2010/11/finding-minimum-window-in-s-which.html,并略微缩短了它(在第一个 <中删除 继续 em> while ,允许简化第二个 的条件,而 循环)。请注意,此解决方案允许在第二个字符串中使用重复项,而上述许多答案都不允许。

private static String minWindow(String s, String t) {
    int[] needToFind = new int[256];
    int[] hasFound = new int[256];

    for(int i = 0; i < t.length(); ++i) {
       needToFind[t.charAt(i)]++;
    }

    int count = 0;
    int minWindowSize = Integer.MAX_VALUE;
    int start = 0, end = -1;
    String window = "";

    while (++end < s.length()) {
        char c = s.charAt(end);
        if(++hasFound[c] <= needToFind[c]) {
           count++;
        }

        if(count < t.length()) continue;
        while (hasFound[s.charAt(start)] > needToFind[s.charAt(start)]) {
           hasFound[s.charAt(start++)]--;
        }

        if(end - start + 1 < minWindowSize) {
           minWindowSize = end - start + 1;
           window = s.substring(start, end + 1);
        }
    }
    return window;
}

答案 2 :(得分:4)

这是我在C ++中的解决方案:

int min_width(const string& x, const set<char>& y) {
  vector<int> at;
  for (int i = 0; i < x.length(); i++)
    if (y.count(x[i]) > 0)
      at.push_back(i);

  int ret = x.size();
  int start = 0;
  map<char, int> count;

  for (int end = 0; end < at.size(); end++) {
    count[x[at[end]]]++;
    while (count[x[at[start]]] > 1)
      count[x[at[start++]]]--;
    if (count.size() == y.size() && ret > at[end] - at[start] + 1)
      ret = at[end] - at[start] + 1;
  }
  return ret;
}

编辑:这是Jack的想法的实现。这与我的复杂性相同,但没有让你困惑的内循环。

int min_width(const string& x, const set<char>& y) {
  int ret = x.size();
  map<char, int> index;
  set<int> index_set;

  for (int j = 0; j < x.size(); j++) {
    if (y.count(x[j]) > 0) {
      if (index.count(x[j]) > 0)
        index_set.erase(index[x[j]]);
      index_set.insert(j);
      index[x[j]] = j;
      if (index.size() == y.size()) {
        int i = *index_set.begin();
        if (ret > j-i+1)
          ret = j-i+1;
      }
    }
  }
  return ret;
}

在Java中,它可以很好地与LinkedHashMap一起实现:

static int minWidth(String x, HashSet<Character> y) {
    int ret = x.length();
    Map<Character, Integer> index = new LinkedHashMap<Character, Integer>();

    for (int j = 0; j < x.length(); j++) {
        char ch = x.charAt(j);
        if (y.contains(ch)) {
            index.remove(ch);
            index.put(ch, j);
            if (index.size() == y.size()) {
                int i = index.values().iterator().next();
                if (ret > j - i + 1)
                    ret = j - i + 1;
            }
        }
    }
    return ret;
}

循环内的所有操作都需要恒定的时间(假设散列元素正确分散)。

答案 3 :(得分:3)

有一个O(这个问题的解决方案)。它在本文中有很好的描述。 http://www.leetcode.com/2010/11/finding-minimum-window-in-s-which.html 希望它有所帮助。

答案 4 :(得分:1)

这是我在C ++中的解决方案,仅供参考。

更新:最初我使用了std :: set,现在我将其更改为tr1 :: unordered_map以将复杂度降低到n ^ 2,否则这两个实现看起来非常相似,以防止这个帖子从太久了,我只列出了改进的解决方案。

#include <iostream>
#include <tr1/unordered_map>
#include <string>

using namespace std;
using namespace std::tr1;

typedef tr1::unordered_map<char, int> hash_t;

// Returns min substring width in which sentence contains all chars in word
// Returns sentence's length + 1 if not found
size_t get_min_width(const string &sent, const string &word) {
    size_t min_size = sent.size() + 1;
    hash_t char_set; // char set that word contains
    for (size_t i = 0; i < word.size(); i++) {
        char_set.insert(hash_t::value_type(word[i], 1));
    }
    for (size_t i = 0; i < sent.size() - word.size(); i++) {
        hash_t s = char_set;
        for (size_t j = i; j < min(j + min_size, sent.size()); j++) {
            s.erase(sent[j]);
            if (s.empty()) {
                size_t size = j - i + 1;
                if (size < min_size) min_size = size;
                break;
            }
        }
    }
    return min_size;
}

int main() {
    const string x = "coobdafceeaxab";
    const string y = "abc";
    cout << get_min_width(x, y) << "\n";
}

答案 5 :(得分:0)

Jack的想法的实现。

public int smallestWindow(String str1, String str2){
        if(str1==null || str2==null){
            throw new IllegalArgumentException();
        }
        Map<String, Node> map=new HashMap<String, Node>();
        Node head=null, current=null;

        for(int i=0;i<str1.length();i++){
            char c=str1.charAt(i);
            if(head==null){
                head=new Node(c);
                current=head;
                map.put(String.valueOf(c), head);
            }
            else{
                current.next=new Node(c);
                current.next.pre=current;
                current=current.next;
                map.put(String.valueOf(c), current);
            }
        }

        Node end=current;
        int min=Integer.MAX_VALUE;
        int count=0;

        for(int i=0;i<str2.length();i++){
            char c = str2.charAt(i);
            Node n=map.get(String.valueOf(c));
            if(n!=null){
                if(n.index==Integer.MAX_VALUE){
                    count++;
                }
                n.index=i;
                if(n==head){
                    Node temp=head;
                    head=head.next;
                    if(head==null){//one node
                        return 1;
                    }
                    head.pre=null;        
                    temp.pre=end;
                    end.next=temp;
                    temp.next=null;
                    end=temp;
                }
                else if(end!=n){
                    n.pre.next=n.next;
                    n.next.pre=n.pre;
                    n.pre=end;
                    n.next=null;
                    end.next=n;
                    end=n;
                }
                if(count==str1.length()){
                    min=Math.min(end.index-head.index+1, min);
                }
            }
        }
        System.out.println(map);
        return min;
    }

答案 6 :(得分:0)

使用滑动窗口的简单java解决方案。扩展NitishMD的想法:

public class StringSearchDemo {

public String getSmallestSubsetOfStringContaingSearchString(String toMatch,
        String inputString) {
    if (inputString.isEmpty() || toMatch.isEmpty()) {
        return null;
    }
    //      List<String> results = new ArrayList<String>(); // optional you can comment this out

    String smallestMatch = "";
    //      String largestMatch = ""; 
    int startPointer = 0, endPointer = 1;
    HashMap<Character, Integer> toMatchMap = new HashMap<Character, Integer>();

    for (char c : toMatch.toCharArray()) {
        if (toMatchMap.containsKey(c)) {
            toMatchMap.put(c, (toMatchMap.get(c) + 1));
        } else {
            toMatchMap.put(c, 1);
        }
    }

    int totalCount = getCountofMatchingString(toMatchMap, toMatch);
    for (int i = 0; i < inputString.length();) {
        if (!toMatchMap.containsKey(inputString.charAt(i))) {
            endPointer++;
            i++;
            continue;
        }
        String currentSubString = inputString.substring(startPointer,
                endPointer);
        if (getCountofMatchingString(toMatchMap, currentSubString) >= totalCount) {
            //              results.add(currentSubString); // optional you can comment this out
            if (smallestMatch.length() > currentSubString.length()) {
                smallestMatch = currentSubString;
            } else if (smallestMatch.isEmpty()) {
                smallestMatch = currentSubString;
            }
            //              if (largestMatch.length() < currentSubString.length()) {
            //                  largestMatch = currentSubString;
            //              }
            startPointer++;
        } else {
            endPointer++;
            i++;
        }

    }
    //      System.out.println("all possible combinations = " + results); // optional, you can comment this out
    //      System.out.println("smallest result = " + smallestMatch);
    //      System.out.println("largest result = " + largestMatch);
    return smallestMatch;
}

public int getCountofMatchingString(HashMap<Character, Integer> toMatchMap,
        String toMatch) {
    int match = 0;
    HashMap<Character, Integer> localMap = new HashMap<Character, Integer>();

    for (char c : toMatch.toCharArray()) {
        if (toMatchMap.containsKey(c)) {
            if (localMap.containsKey(c)) {
                if (localMap.get(c) < toMatchMap.get(c)) {
                    localMap.put(c, (localMap.get(c) + 1));
                    match++;
                }
            } else {
                localMap.put(c, 1);
                match++;
            }
        }
    }
    return match;
}

public static void main(String[] args) {
    String inputString = "zxaddbddxyy由ccbbwwaay漢字由来"; 
    String matchCriteria = "a由";
    System.out.println("input=" + matchCriteria);
    System.out.println("matchCriteria=" + inputString);
    String result = (new StringSearchDemo())
            .getSmallestSubsetOfStringContaingSearchString(matchCriteria, inputString);
    System.out.println("smallest possbile match = " + result);
}

}