leetcode面试准备: Substring with Concatenation of All Words
leetcode面试准备: Substring with Concatenation of All Words
1 题目
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"]
You should return the indices: [0,9].
(order does not matter).
接口:public List<Integer> findSubstring(String s, String[] words)
2 思路
题意
找出可以连接成words
里面单词的组合,输出子字符串的起始位置。
复杂度: Time 😮(n) Space:O(n)
比较复杂的一题,首先是要明确用滑块
的概念来解决,始终保持L集合中的字符串在滑块中都只出现了一次,当然设置一个总计数count
,当count
等于L
集合长度时,即使找了一段符合要求的字符串。
需要用到的内存空间:
- 两张哈希表,一张
map
保存L
集合中的单词,一张curMap
用来保存当前滑块中的单词,key为单词,value为出现次数 count
计数,保存当前滑块中的单词总数left
标记,记录滑块左起点
实现的步骤:
- 遍历一遍单词数组L集合,构造总单词表
map
- 以单词长度为步长
wlen
,遍历目标字符串s
,如果当前单词curStr
在总单词表map
内,则进入步骤3;反之,则清空当前滑块单词表curMap
,将count
置零,将left
移动到下一位置 - 当前滑块档次表中的相应单词计数加1,检查该单词的计数
curMap
是否小于等于总单词表map
中该单词的总数,如果是,则将count
计数加1,进入步骤5;反之,进入步骤4 - 根据左起点
left
收缩滑块,直到收缩到与当前单词相同的字符串片段,将其剔除之后,滑块的收缩工作完成 - 如果当前
count
计数等于单词集合长度L.length
,记录下left
左起点的位置后,将left
右移,当前滑块中相应单词计数减1,总计数减1,继续循环
这里解释下步骤4中的收缩滑块,这是因为当前滑块中有单词的出现次数超过了额定的出现次数,那么就是需要收缩滑块来剔除这个单词,相当于是从滑块的左起点开始寻找该单词,找到之后,将该单词的右端点作为滑块新的左起点,这样就保证了滑块中所有单词都是小于等于额定出现次数,这样也保证了count
计数的有效性。
遇到总单词表中不存在的单词的情况,在步骤2中已经说明,清空当前数据之后继续循环,也就是保证了滑块中是不会出现不存在单词表中的单词的。
最后,考虑最外圈循环,如果是从0开始作为滑块的初始起点,那么其实并没有遍历字符串中的所有可能子串,因为步长是单词长度,所以移动滑块的时候会跨过很多可能子串,所以要在外圈再加一层循环,这个循环的作用就是移动滑块的初始起点,所以循环次数就是单词的长度。
3 代码
public List<Integer> findSubstring(String s, String[] words) {
int slen = s.length();
int wlen = words[0].length();
List<Integer> res = new ArrayList<Integer>();
Map<String, Integer> map = new HashMap<String, Integer>();
for (String word : words) {
if (map.containsKey(word)) {
map.put(word, map.get(word) + 1);
} else {
map.put(word, 1);
}
}
int range = slen - wlen;
for (int i = 0; i < wlen; i++) {
Map<String, Integer> curMap = new HashMap<String, Integer>();
int left = i, count = 0;
for (int j = i; j <= range; j += wlen) {
String curStr = s.substring(j, j + wlen);
if (map.containsKey(curStr)) {
if (curMap.containsKey(curStr)) {
curMap.put(curStr, curMap.get(curStr) + 1);
} else {
curMap.put(curStr, 1);
}
if (curMap.get(curStr) <= map.get(curStr)) {
count++;
} else { // todo
while (true) {
String tmp = s.substring(left, left + wlen);
left += wlen;
curMap.put(tmp, curMap.get(tmp) - 1);
if (tmp.equals(curStr)) {
break;
} else {
count--;
}
}
}
} else {
curMap.clear();
count = 0;
left = j + wlen;
}
if (count == words.length) {
res.add(left);
String tmp = s.substring(left, left + wlen);
curMap.put(tmp, curMap.get(tmp) - 1);
left += wlen;
count--;
}
}
}
return res;
}
4 总结
细节很多,自己纸上走一遍用例,可以明白。
posted on 2015-09-10 12:18 BYRHuangQiang 阅读(218) 评论(0) 编辑 收藏 举报