You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.
For example, given:
s: "barfoothefoobarman"
words: ["foo", "bar"]
You should return the indices: [0,9]
.
(order does not matter).
在这里,我会对这道题目进行分析,并给出处理这类问题的通用模板。
根据题目的意思,我们要找到子串开始的下标,这个子串满足它是words中,所有单词的连接。(单词出现的顺序是无关的)
一个很自然而然的想法是这样的:每次我从一个begin index开始,向后看,如果当前的单词在words中出现且没有被标记,那么我就可以继续往后找,标记了所有的words后,当前的begin index就是我需要的了。如果中途发现单词不合法,我就是继续从下一个begin index开始找。
以题目中的例子为例,一开始,begin index = 0, substring(0, 0 + 3) 为bar,在words中出现了,且被没有被标记,继续往下找,foo,foo也没有被标记,
此时,bar, foo都找到了, begin index = 0就是我想要的结果。
之后,begin index = 1,substring(0 , 0 + 3) 为 arf, 没在words中出现,继续下一个begin index...
在这道题目中:要特别注意的是:words中单词可能会重复,会出现[ "foo", "bar", "foo"]这样的。
代码的运行时间:
代码:
public class SubstringwithConcatenationofAllWords {
private void setCounterOrigin(int[] count, int[] originCount) {
for (int i = 0; i < count.length; i++) {
count[i] = originCount[i];
}
}
public List<Integer> findSubstring(String s, String[] words) {
//prepare counter and indexMap
List<Integer> startList = new ArrayList<>();
Map<String, Integer> countMap = new HashMap<>();
Map<String, Integer> indexMap = new HashMap<>();
for (String str : words) {
int num = 1;
if (countMap.containsKey(str)) {
num += countMap.get(str);
}
countMap.put(str, num);
}
int[] count = new int[countMap.keySet().size()];
int[] originCount = new int[count.length];
int i = 0, start = 0, len = words[0].length();
for (String str : countMap.keySet()) {
indexMap.put(str, i);
originCount[i] = countMap.get(str);
i++;
}
setCounterOrigin(count, originCount);
i = 0;
int countNum = words.length;// check if words are all found
//begin exploring
while (i < s.length() - len + 1) {
String str = s.substring(i, i + len);
if (countMap.containsKey(str)) {
int index = indexMap.get(str);
if (count[index] > 0) { // this if a valid cases
count[index]--;
countNum--;
i = i + len;
} else { // not valid, current start can not form a concatenation, explore from a new start
start++;
i = start;
countNum = words.length;
setCounterOrigin(count, originCount);
}
} else { // not valid, explore from a new start
start++;
i = start;
countNum = words.length;
setCounterOrigin(count, originCount);
}
// check if find all the words
while (countNum == 0) {
startList.add(start); // find one, explore from a new start
start++;
i = start;
countNum = words.length;
setCounterOrigin(count, originCount);
}
}
return startList;
}
}
一个通用的模板参考自:https://discuss.leetcode.com/topic/30941/here-is-a-10-line-template-that-can-solve-most-substring-problems
这个模板利用hashmap和双指针(begin指针标记开始substring的开始, end 指针标记substring的结束),适合来解决所有找出符合某种要求的substring的问题。
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;
}