先看一下我的战况,看看那三个七分钟…,没办法着急了受,其实大部分时间就是花在了调边界条件上了。
然后说一下思路吧:其实就是使用空间换取时间,但是我的时间没有很低,反而空间比我想象的要低一些。步骤如下:
- 我们把words中的所有单词放到哈希表中,其中的键为每个单词,值为单词的次数。
- 然后我们遍历这个字符串s,而且每次遍历的子串长度都应该和words数组中所有字符串长度的总和一样,即
words.length * words[0].length()
。 - 然后我们对子串中每个长度为
words[0].length
的单词进行检查,看看哈希表里面是否有它,或者它是不是已经出现过了:如果没有则直接break,这个子串不符合;如果有而且对应的出现次数大于0,则将出现次数减1。 - 一直到最后如果子串中的每个单词都符合,那么就将这个子串的开始索引放到结果中。否则检查下一个子串。
代码如下:
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> result = new ArrayList<>();
if (s.equals("") || words.length == 0) //被这个
return result;
Map<String, Integer> flag = new HashMap<>();
reset(flag, words); //将所有单词和对应出现次数放进去。
int StrLength = words.length * words[0].length(); //要遍历的子串长度
int head = 0, tail = StrLength, wordLength = words[0].length(); //两个指针指向子串的头和尾
if (wordLength > s.length()) //如果字符串的长度还不如子串长,那么直接返回
return result;
while (tail <= s.length()) { //开始遍历
int index = head;
for (index = head; index < tail; index += wordLength) { //对于子串中的每个单词
String curStr = s.substring(index, index+wordLength); //获取单词
if (!flag.containsKey(curStr) || flag.get(curStr) <= 0) { //单词没有,或者有这个单词但是已经访问过了,不行
break;
} else {
flag.put(curStr, flag.get(curStr)-1); //出现次数减1
}
}
if (index == tail) { //遍历到了最后说明都符合,则把head加到结果集中
result.add(head);
}
head++; //此处子串的前进步长为1
tail++;
reset(flag, words); //每次遍历完一个子串,都要重置访问标志
}
return result;
}
private void reset(Map<String, Integer> flag, String[] words) {
//因为上次遍历后可能flag里面还有一些没访问到,不为0,所以先将出现次数全置为0,然后再统计出现次数。
for (String word : words) {
if (flag.containsKey(word)) {
flag.put(word, 0); //要重置0
}
}
for (int i = 0; i < words.length; ++i)
if (flag.containsKey(words[i])) {
flag.put(words[i], flag.get(words[i])+1); //已经有了,则个数加1
} else {
flag.put(words[i], 1); //否则只放1个
}
}