You are given a string, S, and a list of words, L, that are all of the same length. Find all starting indices of substring(s) in S that is a concatenation of each word in L exactly once and without any intervening characters.
For example, given:
S: "barfoothefoobarman"
L: ["foo", "bar"]
You should return the indices: [0,9]
.
(order does not matter).
Key to Solve: two points Window's range method + HashMap
The idea is similar with Minimum Window Substring Java
the difference is this time we need to scan String by length of word, not
by each Character
1. create a map(Key: String Value: Frequency) of L
2. Move right window point by right + L[0].length
3. to maintain a two points Window's range of left & right
This time the partial match will be one or two words from L,
and Full match will be all the words of List were found in S
we need to Create another currMap to compare with map of L
when we find any match
4. Move left window point by left + L[0].length under condition of
4.1 skip non-match => currMap.clear & left=right+L[0].length
4.2 partial match after reduce frequency in currMap
4.2 full-match after reduce frequency in currMap &
store beginning index into ArrayList
As a result:
Time Complexity: O(2*(N/l)*l)=O(N) [l is length of word] because
we scan by length of word each time and 2 times in total(one left
point, one is right point)
Space Complexity: O(M*l) [M is size of wordsList
Check the Coding in detail below:
public class Solution {
public ArrayList<Integer> findSubstring(String S, String[] L) {
ArrayList<Integer> result=new ArrayList<Integer>();
if(S.length()==0 || L.length==0) return result;
HashMap<String,Integer> map=new HashMap<String, Integer>();
int lenL=L.length;
for(int i=0;i<lenL;i++){
if(map.containsKey(L[i])){
map.put(L[i],map.get(L[i])+1);
}else {
map.put(L[i], 1);
}
}
int wordLength=L[0].length();
for(int i=0;i<wordLength;i++){
int count=0;
HashMap<String, Integer> currMap=new HashMap<String, Integer>();
int left=i;
for(int right=i;right<=S.length()-wordLength;right+=wordLength) {
String str=S.substring(right,right+wordLength);
if(map.containsKey(str)){
//establish currMap
if(currMap.containsKey(str)){
currMap.put(str, currMap.get(str)+1);
}else{
currMap.put(str,1);
}
if(map.get(str) < currMap.get(str)){
// partial match were found
while (map.get(str) < currMap.get(str)) {
String temp = S.substring(left, left + wordLength);
currMap.put(temp, currMap.get(temp) - 1);
left += wordLength;
}
}
else{
count++;
}
//full match were found
if(count==L.length){
result.add(left);
String temp=S.substring(left,left+wordLength);
currMap.put(temp,currMap.get(temp)-1);
count--;
left+=wordLength;
}
}else {
currMap.clear();
count=0;
//skip non-match String by left=right+L[0].length
left=right+wordLength;
}
}
}
return result;
}
}