题意:给定一个字符串s和一个字符串数组words,找出s中所有以words中全部字符串拼接成的字符串作为子串的起始索引并返回。
For example, given:
s: “barfoothefoobarman”
words: [“foo”, “bar”]
You should return the indices: [0,9].
(order does not matter).
注意:words中的元素是有可能重复的。
思路:令 len = words[0].length;
满足题意的子串是words中的各元素以任意顺序组合而成,引入滑动窗口的概念,滑动窗口从左至右遍历s,每次“读入”一个单词,最后滑动窗口包含的子串就是满足题意的子串。
(1)使用Map 1把words中出现的字符串和次数记录下来。
(2)滑动窗口初始大小为0,每次读入一个len长度的字符串,另外使用一个Map 2维护窗口中的字符串和出现的次数。
(3)窗口每次读入一个len长度字符串后,需要判断这个字符串是否是words中出现的(利用第(1)步中的Map 1),如果Map 1中不包含这个字符串,则窗口需要初始化为0,转到第(2)步,重新读入下一个字符串;如果Map 1中包含这个字符串,则把这个字符串记录到Map 2中,转到第(4)步。
(4)判断这个字符串出现的次数是否大于在Map 1中保存的次数,如果大于,则窗口需要从左侧收缩,直到次数等于Map 1中的次数;如果不大于,转到第(5)步。
(5)判断窗口的宽度是否等于len * words.length,如果等于,表明此窗口是满足题意的,返回初始索引;如果不等于,继续读入,直到等于。继续构造下一个窗口。
下图为一个示意图,假设words中有3个元素。
叙述真的很难,贴上代码吧
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] words = {"foo", "bar",};
String s = "barfoothefoobarman";
List<Integer> list = findSubstring(s, words);
for (int i : list)
System.out.print(i + " ");
}
public static List<Integer> findSubstring(String s, String[] words){
int lenOfWord = words[0].length();
int len = words.length;
List<Integer> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
for (String a : words) {
map.put(a, map.containsKey(a) ? map.get(a) + 1 : 1);
}
for (int i = 0; i < lenOfWord; i++) {
Map<String, Integer> window = new HashMap<>();
int left = i, right = i;
while (right <= s.length() - lenOfWord) {
String word = s.substring(right, right + lenOfWord);
right += lenOfWord;
if (map.containsKey(word)) {
window.put(word, window.containsKey(word) ? window.get(word) + 1 : 1);
while (window.get(word) > map.get(word)) {
String head = s.substring(left, left + lenOfWord);
left += lenOfWord;
window.put(head, window.get(head) - 1);
}
} else {
window.clear();
left = right;
}
if (right - left == len * lenOfWord) {
list.add(left);
window.clear();
left += lenOfWord;
right = left;
}
}
}
return list;
}
}
稍微解释一下为什么外层循环只需要 i 从 0 到 lenOfWord - 1吧,因为当 i 变为 lenOfWord 之后,情形就和 i 为 0 时一样了。
后续再改这篇文章吧,感觉一时半会儿也讲不清,后面会尽量多贴图说明的。