在字符串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)
。
答案 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);
}
}