题目: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).
这道题想了蛮久,主要是coding过程中老是出一些边界的错误。第一眼看到,立(zi)马(zuo)想(cong)到(ming)的解法是不是可以用bit位来表示L中的各个key呀,不同的key代表不同的位,然后通过扫描S,通过与操作确定各个bit位是不是在长度为L的substring中。结果写了一会发现问题是,L中有可能有相等的字符串哦。。。那在搜索S的时候就悲剧了不是。
一个折中的方法是,对每个key在L中的,保存一个list,例如有L=["foo","bar","foo"],那我们做这样一个hash哇:
foo: 0->1
bar: 2
然后在搜索S的时候顺次检查一个bitset。代码如下:
解法一:
1 public static ArrayList<Integer> findSubstring2(String S, String[] L) { 2 int lengthPerKey = L[0].length(); 3 int numberOfKeys = L.length; 4 BitSet bitSet = new BitSet(numberOfKeys); 5 HashMap<String, ArrayList<Integer>> map = new HashMap<String, ArrayList<Integer>>(); 6 ArrayList<Integer> result = new ArrayList<Integer>(); 7 int k = 0; 8 for(String str : L){ 9 if(!map.containsKey(str)){ 10 ArrayList<Integer> list = new ArrayList<Integer>(); 11 list.add(k++); 12 map.put(str, list); 13 }else { 14 map.get(str).add(k++); 15 } 16 } 17 18 for(int i = 0; i <= S.length() - numberOfKeys*lengthPerKey; i++){ 19 bitSet.clear(); 20 int j = 0; 21 int st = i; 22 for(j = 0; j < numberOfKeys;j++){ 23 if(S.length() - st < (numberOfKeys - j)*lengthPerKey) break; 24 ArrayList<Integer> list = map.get(S.substring(st, st + lengthPerKey)); 25 if(list == null)break; 26 k = 0; 27 while(k < list.size() && bitSet.get(list.get(k))) k++; 28 if(k < list.size()) 29 bitSet.set(list.get(k)); 30 else break; 31 st += lengthPerKey; 32 } 33 if(bitSet.cardinality() == numberOfKeys) { 34 result.add(i); 35 } 36 } 37 return result; 38 }
不幸的是,大集合超时了。我去。。。
仔细想想,非要用bitset么?特别是扫描的inner loop,还要遍历hash的value(list)。完全可以用一个计数代替么。不用遍历list,对于重复出现的,我们search S的时候,只是减去计数1.
解法二:
1 public static ArrayList<Integer> findSubstring3(String S, String[] L) { 2 int lengthPerKey = L[0].length(); 3 int numberOfKeys = L.length; 4 HashMap<String, Integer> map = new HashMap<String, Integer>(); 5 ArrayList<Integer> result = new ArrayList<Integer>(); 6 for(String str : L){ 7 if(!map.containsKey(str)){ 8 map.put(str, 1); 9 }else { 10 map.put(str, map.get(str) + 1); 11 } 12 } 13 14 for(int i = 0; i <= S.length() - numberOfKeys*lengthPerKey; i++){ 15 int j = 0; 16 int st = i; 17 HashMap<String, Integer> wordsCount = new HashMap<String, Integer>(map); 18 for(j = 0; j < numberOfKeys;j++){ 19 if(S.length() - st < (numberOfKeys - j)*lengthPerKey) break; 20 String sub = S.substring(st,st+lengthPerKey); 21 if(wordsCount.containsKey(sub)){ 22 int times = wordsCount.get(sub); 23 if(times == 1) wordsCount.remove(sub); 24 else wordsCount.put(sub, times - 1); 25 }else break; 26 st += lengthPerKey; 27 } 28 if(j == numberOfKeys){ 29 result.add(i); 30 } 31 } 32 return result; 33 }
例如L=["foo","bar","foo"],我们的hash是这样的:
foo: 2
bar: 1
然后对S中每一个固定长度的substring做计数。
总结:
1.如果你用了超过3个(包括3个)复杂数据结构,应该思考是不是自己想多了;
2.想多了会毁了你。
3.不要装b,先理解问题再想解法而不是为了用某个算法。