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”], return [0,9].
Analysis
This problem is similar (almost the same) to Longest Substring Which Contains 2 Unique Characters.
Since each word in the dictionary has the same length, each of them can be treated as a single character.
Java Solution
public List<Integer> findSubstring(String s, String[] words) { ArrayList<Integer> result = new ArrayList<Integer>(); if(s==null||s.length()==0||words==null||words.length==0){ return result; } //frequency of words HashMap<String, Integer> map = new HashMap<String, Integer>(); for(String w: words){ if(map.containsKey(w)){ map.put(w, map.get(w)+1); }else{ map.put(w, 1); } } int len = words[0].length(); for(int j=0; j<len; j++){ HashMap<String, Integer> currentMap = new HashMap<String, Integer>(); int start = j;//start index of start int count = 0;//count totoal qualified words so far for(int i=j; i<=s.length()-len; i=i+len){ String sub = s.substring(i, i+len); if(map.containsKey(sub)){ //set frequency in current map if(currentMap.containsKey(sub)){ currentMap.put(sub, currentMap.get(sub)+1); }else{ currentMap.put(sub, 1); } count++; while(currentMap.get(sub)>map.get(sub)){ String left = s.substring(start, start+len); currentMap.put(left, currentMap.get(left)-1); count--; start = start + len; } if(count==words.length){ result.add(start); //add to result //shift right and reset currentMap, count & start point String left = s.substring(start, start+len); currentMap.put(left, currentMap.get(left)-1); count--; start = start + len; } }else{ currentMap.clear(); start = i+len; count = 0; } } } return result; } |
why is the first loop from 0 to length of the longest word.
for(int j=0; j<len; j++)
Why isn't this 0 to the length of the input string s? I'm guessing that you want to find all possibilities with the search starting at each index of the input string. well 0 to the length of the input string
for(int j=0; j<s.length()-len; j++)
for(int i=j; i<=s.length()-len; i=i+len)
Thanks,
Derek
I solved by a better solution which happens to be more clear and has a better performance using the indexOf method and comparing the indices as follows:
public static List searchIndecies(String s, String arr[]) {
if (s == null || s.length() == 0 || arr == null || arr.length == 0) {
return null;
}
int wordLength = arr[0].length();
List returnedIndecies = new ArrayList();
List indecies = new ArrayList();
for (int i = 0; i < s.length(); i++) {
indecies.clear();
for (int m = 0; m < arr.length; m++) {
int index = s.indexOf(arr[m], i);
if (index != -1) {
indecies.add(index);
}
}
Collections.sort(indecies);
boolean validSeq = false;
for (int n = 1; n < indecies.size() && indecies.size() == arr.length; n++) {
if (indecies.get(n – 1) != indecies.get(n) – wordLength) {//Comparing indices based on words same length
i = i + wordLength – 1;
break;
} else {
i = indecies.get(n);
validSeq = true;
}
}
if (validSeq) {
returnedIndecies.add(indecies.get(0));
}
}
return returnedIndecies;
}
A simple to understand soluton.
Just get every possible concatenation of words in words array, and check whether each occurs in the main string.
if the result is not -1, we have an index of substring!
import java.util.*;
public class duplicate_substring{
public static ArrayList allPossibleArrangements = new ArrayList();
public static void main(String[] args){
String[] arr = new String[] {"chinelo", "arukwe", "larry"};
ArrayList words = new ArrayList(Arrays.asList(arr));
String s = "chineloarukwelarryokekearukwechinelolarrytestinglarryarukwechinelo";
permutate(words, new Stack(), arr.length);
List solution = solution(allPossibleArrangements.toArray(new String[0]), s);
System.out.println(solution.toString());
}
public static List solution(String[] words, String s){
//use combinatorial algorithm to get every possible concatenation of words
//find the index of each derived word in s.
StringBuilder build = new StringBuilder();
ArrayList result = new ArrayList();
for(int i = 0; i < words.length; i++){
String currentConcatenation = words[i];
if(s.indexOf(currentConcatenation)!=-1){
result.add(s.indexOf(currentConcatenation));
}
build.setLength(0);
}
return result;
}
public static void permutate(List items, Stack permutation, int size) {
/* permutation stack has become equal to size that we require */
if(permutation.size() == size) {
/* print the permutation */
//System.out.println(Arrays.toString(permutation.toArray(new Integer[0])));
String s = "";
for(String str: permutation){
s+=str;
}
allPossibleArrangements.add(s);
}
/* items available for permutation */
String[] availableItems = items.toArray(new String[0]);
for(String i : availableItems) {
/* add current item */
permutation.push(i);
/* remove item from available item set */
items.remove(i);
/* pass it on for next permutation */
permutate(items, permutation, size);
/* pop and put the removed item back */
items.add(permutation.pop());
}
}
}