题目:

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 wordsexactly 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).

链接: http://leetcode.com/problems/substring-with-concatenation-of-all-words/

题解:

第一反应是用Trie,像spell checker一样,在Trie中检查这段text是不是word。 (待补充)

第二反应是做一个类似于DFA的状态机。 (待补充)

第三反应是看答案。默默看答案,看到大家都用HashMap,所以也写了用两个HashMap的, 提交就超时了。再想一想,还是要用sliding window, 比如text = abca, 单词为'a','b'和'c',这样0和1都是有效index,要减少重复compare.

HashMap:

先把word以及word count放入一个wordMap中,然后对text进行遍历。遍历的时候,每次步长为wordLength,所以对于整个text我们只需要遍历wordLength次pass。 对于每次pass,由于要找到所有复合条件的index,所以我们进行sliding window。为此我们还需要一个current map用来记录当前的window,一个lo变量来记录window的左边界,以及一个。之后对于每次pass,先看当前的单词是否存在于wordMap里,假如不存在则reset curMap,count和lo。假如存在,则把当前单词加入现在的window里。加入完毕后还需要检查重复情况,假如当前window里这个单词的计数大于wordMap里的计数,则从window左边界逐个取出单词,直到当前单词的计数等于wordMap里的计数为止。 假如count == 单词总数,则 lo 是一个解,加入到结果list里,更新lo,count,并且window向右移动一个单词。 代码写得很拖沓,有空要好好refactor。

Time Complexity - O(n), Space Complexity - O(m * l), m为单词数量,l为单词长度。

public class Solution {
HashMap<String, Integer> wordMap; public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if(s == null ||s.length() == 0 || words == null || words.length == 0)
return res;
wordMap = new HashMap<>();
fillWordMap(words); //put all words and their frequencey to wordMap
int wordLen = words[0].length(), wordCount = words.length;
HashMap<String, Integer> curMap = new HashMap<>(); //sliding window storing current word for(int i = 0; i < wordLen; i++) { //we are going to process s word by word, so totally we need "wordLen" passes
int j = i, lo = i, count = 0; //lo is the index we need to record and add to result list
curMap.clear(); while(j <= s.length() - wordLen) {
String curWord = s.substring(j, j + wordLen);
if(!wordMap.containsKey(curWord)) { // intervening characters found
curMap.clear();
count = 0;
lo = j + wordLen;
} else {
if(curMap.containsKey(curWord)) //put current word into current window
curMap.put(curWord, curMap.get(curWord) + 1);
else
curMap.put(curWord, 1);
count++; while(curMap.get(curWord) > wordMap.get(curWord)) { // remove words from left end of the window until valid
String rmvWord = s.substring(lo, lo + wordLen);
curMap.put(rmvWord, curMap.get(rmvWord) - 1);
count--;
lo += wordLen;
} if(count == wordCount) { //if target string found
res.add(lo);
String loWord = s.substring(lo, lo + wordLen);
curMap.put(loWord, curMap.get(loWord) - 1);
count--;
lo += wordLen;
}
} j += wordLen;
}
} return res;
} private void fillWordMap(String[] words) {
for(String word : words) {
if(wordMap.containsKey(word))
wordMap.put(word, wordMap.get(word) + 1);
else
wordMap.put(word, 1);
}
}
}

Trie:

DFA:

Histogram:

二刷:

这里主要还是跟第一遍相同。先建立一个global的wordMap,里面还有单词以及个数。接下来做双重循环,外循环是从0 到 单词的长度,每次递增一个字符,内循环开始前我们clear curMap。内循环是从j = i开始,每次递增一个单词长度L。同时我们维护一个滑动窗口的左边界lo,以及当前复合条件的单词数目 count。 每次我们先求出当前的单词 - s.substring(j, j + wordLen),先判断其是否在wordMap里,假如不在,我们可以直接跳过L - 当前单词,从下一个单词其实为止开始查找 (这里我们要清空curMap以及count,更新lo)。假若当前单词在wordMap里, 那么我们把它加入到curMap中,之后再拿curMap中这个单词的value与wordMap中这个单词的value进行比较。假如curMap.value小,那么我们继续下面的计算。假如curMap.get(curWord) > wordMap.get(curWord),说明我们加入了多余的单词,这里我们要用类似"Sliding Window Maximum"中的方法,使用一个while循环,将这个window前部的单词一个一个poll出去。poll的过程就是先求出前部单词 s.substring(lo, lo + wordLen),然后在curMap中将其value - 1,并且count--,之后再更新lo = lo + wordLen来比较下一个首部单词。直到我们把多加入的单词去掉,使得curMap.get(curWord) <= wordMap.get(curWord)为止。  最后当count == words.length时,这时我们找到了一个解,把这个解的开头index lo加入到结果集中。然后我们要把window首部单词去掉,count--,并且增加lo = lo + wordLen,来继续进行下面的判断。

Java:

假如不考虑substring的话,应该是L次遍历,每次遍历 n / L个字符,这样应该算是 Time Complexity:  O(n), Space Complexity - O(L * m), L为单词的长度,m为单词个数。

public class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) {
return res;
}
Map<String, Integer> wordMap = new HashMap<>();
for (String word : words) {
if (!wordMap.containsKey(word)) {
wordMap.put(word, 1);
} else {
wordMap.put(word, wordMap.get(word) + 1);
}
}
int wordLen = words[0].length();
Map<String, Integer> curMap = new HashMap<>(); for (int i = 0; i < wordLen; i++) { // start from each char
int lo = i, count = 0;
curMap.clear();
for (int j = i; j <= s.length() - wordLen; j += wordLen) {
String curWord = s.substring(j, j + wordLen);
if (!wordMap.containsKey(curWord)) {
curMap.clear();
count = 0;
lo = j + wordLen;
} else {
if (!curMap.containsKey(curWord)) {
curMap.put(curWord, 1);
} else {
curMap.put(curWord, curMap.get(curWord) + 1);
}
count++;
while (curMap.get(curWord) > wordMap.get(curWord)) { // poll from front
String wordToRemove = s.substring(lo, lo + wordLen);
curMap.put(wordToRemove, curMap.get(wordToRemove) - 1);
lo += wordLen;
count--;
}
if (count == words.length) { // found one solution
res.add(lo);
String loWord = s.substring(lo, lo + wordLen);
curMap.put(loWord, curMap.get(loWord) - 1);
lo += wordLen;
count--;
}
}
}
}
return res;
}
}

有的时候HashMap的操作也可以简写,比如

curMap.put(curWord, curMap.get(curWord) == null ? 1 : curMap.get(curWord) + 1);

三刷:

跟二刷基本相同。

要注意的是  j的范围是  [i,  s.length() - wordLen],前后都是闭合的。

Java:

public class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if (s == null || words == null || words.length == 0) return res;
Map<String, Integer> wordsMap = new HashMap<>();
for (String word : words) {
if (!wordsMap.containsKey(word)) wordsMap.put(word, 1);
else wordsMap.put(word, wordsMap.get(word) + 1);
}
int wordLen = words[0].length(); for (int i = 0; i < wordLen; i++) {
Map<String, Integer> curMap = new HashMap<>();
int lo = i;
int count = 0;
for (int j = i; j <= s.length() - wordLen; j += wordLen) {
String word = s.substring(j, j + wordLen);
if (!wordsMap.containsKey(word)) {
count = 0;
curMap.clear();
lo = j + wordLen;
continue;
}
if (!curMap.containsKey(word)) curMap.put(word, 1);
else curMap.put(word, curMap.get(word) + 1);
count++;
while (curMap.get(word) > wordsMap.get(word)) {
String loWord = s.substring(lo, lo + wordLen);
curMap.put(loWord, curMap.get(loWord) - 1);
lo += wordLen;
count--;
}
if (count == words.length) {
res.add(lo);
String loWord = s.substring(lo, lo + wordLen);
curMap.put(loWord, curMap.get(loWord) - 1);
lo += wordLen;
count--;
}
}
}
return res;
}
}

30. Substring with Concatenation of All Words的更多相关文章

  1. LeetCode - 30. Substring with Concatenation of All Words

    30. Substring with Concatenation of All Words Problem's Link --------------------------------------- ...

  2. [Leetcode][Python]30: Substring with Concatenation of All Words

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 30: Substring with Concatenation of All ...

  3. [LeetCode] 30. Substring with Concatenation of All Words 解题思路 - Java

    You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...

  4. leetCode 30.Substring with Concatenation of All Words (words中全部子串相连) 解题思路和方法

    Substring with Concatenation of All Words You are given a string, s, and a list of words, words, tha ...

  5. LeetCode HashTable 30 Substring with Concatenation of All Words

    You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...

  6. [LeetCode] 30. Substring with Concatenation of All Words 串联所有单词的子串

    You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...

  7. Java [leetcode 30]Substring with Concatenation of All Words

    题目描述: You are given a string, s, and a list of words, words, that are all of the same length. Find a ...

  8. 【LeetCode】30. Substring with Concatenation of All Words

    You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...

  9. 【一天一道LeetCode】#30. Substring with Concatenation of All Words

    注:这道题之前跳过了,现在补回来 一天一道LeetCode系列 (一)题目 You are given a string, s, and a list of words, words, that ar ...

  10. [leetcode]30. Substring with Concatenation of All Words由所有单词连成的子串

    You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...

随机推荐

  1. 初识Style和Theme

    初识Style和Theme 学习自 http://www.jcodecraeer.com/a/basictutorial/2016/0812/6533.html 认识Style 大家还记得如何设置一个 ...

  2. C# GUID生成

    System.Guid.NewGuid().ToString()

  3. python 列表返回重复数据的下标

    class Solution(object): def searchRange(self, nums, target): """ :type nums: List[int ...

  4. 关于文档模式、DCOTYPE声明及严格模式

    1.文档模式 文档模式的概念是由IE5.5引入,通过使用文档类型(DOCTYPE)切换实现的.不同的文档模式主要影响CSS内容的呈现,尤其是浏览器对盒模型的解析,但在某些情况下也会影响到JavaScr ...

  5. 洛谷.3374.[模板]树状数组1(CDQ分治)

    题目链接 简易CDQ分治教程 //每个操作分解为一个有序数对(t,p),即(时间,操作位置),时间默认有序,用CDQ分治处理第二维 //对于位置相同的操作 修改优先于查询 //时间是默认有序的 所以可 ...

  6. [HackerRank]Choosing White Balls

    [HackerRank]Choosing White Balls 题目大意: 有\(n(n\le30)\)个球排成一行,每个球的颜色为黑或白. 执行\(k\)次操作,第\(i\)次操作形式如下: 从\ ...

  7. 解决 ajax 不能下载文件的问题

  8. FTP和TCP的文件传输效率对比测试分析

    前言 最近因项目需要,需要把一定数量的中等文件从开发板上传到电脑上,分别选择了FTP和TCP自定义协议两种方式进行传输,进行了简单的对比测试,故做如下记录. 测试环境 开发板:Linux,ARMv7 ...

  9. 菜鸟nginx源代码剖析数据结构篇(八) 缓冲区链表ngx_chain_t

    菜鸟nginx源代码剖析数据结构篇(八) 缓冲区链表 ngx_chain_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog. ...

  10. Maven deploy部署jar到远程私服仓库

    一.配置私服账号密码 修改maven配置文件,在$MAVEN_HOME/conf/setting.xml中增加如下配置: 注意,这里配置的id为releases何snapshots,当然可以改为其他, ...