您的位置:首页 > 其它

76. Minimum Window Substring && 438. Find All Anagrams in a String

2016-11-20 20:50 405 查看
这个题和Longest
Substring Without Repeating Characters及Substring
with Concatenation of All Words类似。

在Discuss里面有个帖子都总结出这类问题的一个template了,但是写的比较难懂,下面的回复有一个解释的比较清楚,博主也是直接把他的code用Java重新写了一遍。

本来想在初始化map的时候直接把1到2^16这所有的char先放进map,超时,可能创建map耗时过长,只能需要用到什么就创建并put进map

对于需要的字符,因为初始化的时候已经设定好了需要的数量,所以当需要他们的数量全部变为0的时候就是一个可以覆盖的window(但可能包含冗余),在缩小window大小的时候,如果把需要的字符串缩掉了,说明就已经达到了最大能缩小的限度,

而对于不需要的字符,在滑动窗口的时候记录的就是他们在窗口中多出来的量(表示为负值),在取出冗余的过程中,他们永远不会出现需求大于0的情况,因为你最多最多把窗口缩小为0,这个时候那些不用的字符自是变为0而已,所以不需要的字符在Map中一直都是负的或者0

所以一个trick就是在初始化的时候设置需要的字符数量,窗口中无关的字符也把他们放在Map里面

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 empty string 
""
.

If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.

import java.util.HashMap;
import java.util.Map;

public class Solution {
public String minWindow(S
d201
tring s, String t) {
// 初始化map
Map<Character, Integer> map = new HashMap<Character, Integer>();
//        for(int i=0; i<=Character.MAX_CODE_POINT; i++) {
//        	map.put((char)i, 0);
//        }

char[] ss = s.toCharArray();
char[] ts = t.toCharArray();
for(char c : ts)	map.put(c, map.containsKey(c) ? map.get(c)+1 : 1);

// counter是待match的字符数量
int start = 0, end = 0, counter = t.length(), minStart = 0, minLen = Integer.MAX_VALUE;

// 移动end直到找到一个正确的window
while(end < s.length()) {
// 如果当前的window还需要ss[end]这个字符
if(map.containsKey(ss[end]) && map.get(ss[end]) > 0)
counter --;

// 因为添加到了window,所以对当前添加的字符减1,不管需不需要这个字符
// 因为后面缩小window可能会用到这个字符
map.put(ss[end], map.containsKey(ss[end]) ? map.get(ss[end]) - 1 : -1);

// end任务完成,可以加1
// 当当前window满足时,end++仍然会执行
end ++;

// 如果找到了合适的window,就开始移动start来找到更小的window
while(counter == 0) {
if(end - start < minLen) {
minLen = end - start;
minStart = start;
}

// 将要移除start位置上的字符
map.put(ss[start], map.containsKey(ss[start]) ? map.get(ss[start])+1 : 1);

// 如果window中间没有跟ss[start]相等的字符补充,那么这个window就不行,跳出循环继续end++找
if(map.containsKey(ss[start]) && map.get(ss[start]) > 0)
counter ++;

start ++;
}
}

if(minLen != Integer.MAX_VALUE)
return s.substring(minStart, minStart + minLen);

return "";
}
}


模板:

For most substring problem, we are given a string and need to find a substring of it which satisfy some restrictions. A general way
is to use a hashmap assisted with two pointers. The template is given below.

int findSubstring(string s){
vector<int> map(128,0);
int counter; // check whether the substring is valid
int begin=0, end=0; //two pointers, one point to tail and one  head
int d; //the length of substring

for() { /* initialize the hash map here */ }

while(end<s.size()){

if(map[s[end++]]-- ?){  /* modify counter here */ }

while(/* counter condition */){

/* update d here if finding minimum*/

//increase begin to make it invalid/valid again

if(map[s[begin++]]++ ?){ /*modify counter here*/ }
}

/* update d here if finding maximum*/
}
return d;
}


Given a string s and a non-empty string p, find all the start indices of p's anagrams in s.

Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100.

The order of output does not matter.

Example 1:
Input:
s: "cbaebabacd" p: "abc"

Output:
[0, 6]

Explanation:
The substring with start index = 0 is "cba", which is an anagram of "abc".
The substring with start index = 6 is "bac", which is an anagram of "abc".


Example 2:
Input:
s: "abab" p: "ab"

Output:
[0, 1, 2]

Explanation:
The substring with start index = 0 is "ab", which is an anagram of "ab".
The substring with start index = 1 is "ba", which is an anagram of "ab".
The substring with start index = 2 is "ab", which is an anagram of "ab".

思路:有了上面的模板,套进去就OK了

/*
* 想到了满足一定条件的substring问题有个模板,HashMap + start end two pointers
* https://discuss.leetcode.com/topic/30941/here-is-a-10-line-template-that-can-solve-most-substring-problems */
public class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> rst = new ArrayList<Integer>();
char[] cs = s.toCharArray();

// initialize map
Map<Character, Integer> map = new HashMap<Character, Integer>();
for(char c : p.toCharArray())
map.put(c, map.containsKey(c)?map.get(c)+1:1);

// two pointers
int start = 0, end = 0, count = p.length();

while(end < s.length()) {
// modify counter to find a valid but large window
if(map.containsKey(cs[end]) && map.get(cs[end]) > 0)
count--;
map.put(cs[end], map.containsKey(cs[end])?map.get(cs[end])-1:-1);

end++;

// if found a valid but large window, try to minimize its length
while(count == 0) {

// specific restrictions
if(end - start == p.length())
rst.add(start);

// remove start and check valid and update count
map.put(cs[start], map.get(cs[start])+1);
if(map.get(cs[start]) > 0)
count ++;
start++;
}
}

return rst;
}
}

不过这里有更严格的限制,可以写得更加简洁

public List<Integer> findAnagrams(String s, String p) {
List<Integer> list = new ArrayList<>();
if (s == null || s.length() == 0 || p == null || p.length() == 0) return list;
int[] hash = new int[256]; //character hash
//record each character in p to hash
for (char c : p.toCharArray()) {
hash[c]++;
}
//two points, initialize count to p's length
int left = 0, right = 0, count = p.length();
while (right < s.length()) {
//move right everytime, if the character exists in p's hash, decrease the count
//current hash value >= 1 means the character is existing in p
if (hash[s.charAt(right++)]-- >= 1) count--;

//when the count is down to 0, means we found the right anagram
//then add window's left to result list
if (count == 0) list.add(left);

//if we find the window's size equals to p, then we have to move left (narrow the window) to find the new match window
//++ to reset the hash because we kicked out the left
//only increase the count if the character is in p
//the count >= 0 indicate it was original in the hash, cuz it won't go below 0
if (right - left == p.length() && hash[s.charAt(left++)]++ >= 0) count++;
}
return list;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: