leetcode面试准备: Substring with Concatenation of All Words
2015-09-10 12:18
483 查看
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 :O(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 总结
细节很多,自己纸上走一遍用例,可以明白。相关文章推荐
- 面试整理(二)
- 史上最全的iOS面试题及答案
- 【剑指offer】面试题42:左旋转字符串
- 面试常问的40个问题附带答案!(前10道题)
- 《招聘一个靠谱的iOS》|| 文章 || 面试题 || 参考答案
- Java泛型问答——面试
- Android 面试题-Android系统相关
- Android 面试题-四大组件
- 程序员如何实现自我增值
- 《招聘一个靠谱的iOS》|| 文章 || 面试题 || 参考答案
- 程序员的 13 种噩梦,你遇到过哪些?
- 【剑指offer】面试题41:和为S的连续子序列
- 程序员如何持续提升自己的开发技能
- 程序员的激情其实是一种痛苦
- 黑马程序员—–常用工具类
- 轻松搞定面试中的二叉树题目
- 程序员是如何成为框架设计师或者是项目经理的?
- String 的面试题
- 一位程序员工作10年总结的13个忠告
- 代码面试最常用的10大算法