Substring with Concatenation of All Words
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 wordsexactly 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).
在这里,得以复习hashmap,并利用数组条件,减少使用map接口的频率,从而降低时间消耗。
由于存在重复项的问题,为此,我们设立两个map,一个记录words的字符串出现次数,一个作为临时map,记录每次截取的字符串个数。若出现不是第一个map的子串则跳过,若扫描个数出现大于第一个map的个数则跳过。若完成遍历的时候,计数与words长度相同,不需要map的相等判断,直接可以判断符合条件。因为,不符合的情况,已经全部被过滤。
public List<Integer> findSubstring(String s, String[] words) { int len = s.length(); int wlen = words.length; int slen =words[0].length(); int end = len-wlen*slen; //判断是否都满足子串数组 HashMap<String, Integer> map = new HashMap<String, Integer>(); HashMap<String, Integer> map1 = new HashMap<String, Integer>();//临时map List<Integer> ls =new ArrayList<Integer>(); for (int i = 0; i < wlen; i++) { if(map.containsKey(words[i])){ map.put(words[i], map.get(words[i])+1); }else{ map.put(words[i], 1); } } int i=0; while(i<=end){ String str = s.substring(i, i+slen); if(map.containsKey(str)){ //判断满足出现的组合contain if(map1.containsKey(str)){ map1.put(str, map1.get(str)+1); }else{ map1.put(str, 1); } int t =i+slen; int j; for ( j = 1; j < wlen; j++) { if(t+slen>len){ return ls; } String str1 = s.substring(t, t+slen); if(map.containsKey(str1)){ if(map1.containsKey(str1)){ map1.put(str1, map1.get(str1)+1); }else{ map1.put(str1, 1); } }else{ break; } t+=slen; if(map1.get(str1)>map.get(str1)){//这个 break; } } if(j==wlen){//这个 ls.add(i); } } i++; map1.clear(); } return ls; }
上述对算法的优化参考自http://www.cnblogs.com/zhaolizhen/p/SubCon.html
这里,再给出一个discuss上的O(n)的解法:
public List<Integer> findSubstring(String S, String[] L) { List<Integer> res = new ArrayList<>(); if (L.length == 0) { return res; } int len = L[0].length(); int num = L.length; if (len * num > S.length()) { return res; } //histogram of words in L HashMap<String, Integer> dic = new HashMap<>(); for (String s : L) { if (dic.containsKey(s)) { dic.put(s, dic.get(s) + 1); } else { dic.put(s, 1); } } //the word that starts from i in S String[] sDic = new String[S.length() - len + 1]; for (int i = 0; i < sDic.length; i++) { String sub = S.substring(i, i + len); if (dic.containsKey(sub)) { sDic[i] = sub; } else { sDic[i] = ""; } } //traverse in order of 0,0+len,...,1,1+len,...len-1,len-1+len...therefore it is O(n) despite of two loops for (int i = 0; i < len; i++) { //start of concatenation int start = i; //number of words found int found = 0; //dynamic word histogram of words in substring(start,j); HashMap<String, Integer> tempDic = new HashMap<>(); for (int j = i; j <= S.length() - len; j = j + len) { String word = sDic[j]; if (word.equals("")) { tempDic = new HashMap<>(); start = j + len; found = 0; continue; } else { if (!tempDic.containsKey(word)) { tempDic.put(word, 1); } else { tempDic.put(word, tempDic.get(word) + 1); } found++; } //if we over-count a word, delete the first word in front. Also delete the words before that. if (tempDic.get(word) > dic.get(word)) { while (!sDic[start].equals(word)) { tempDic.put(sDic[start], tempDic.get(sDic[start]) - 1); start += len; found--; } tempDic.put(word, tempDic.get(word) - 1); start += len; found--; } if (found == num) { res.add(start); } } } return res; }