Leetcode - Substring with Contentaion of All Words

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

[分析] 方法2的分析转载在 http://segmentfault.com/a/1190000002625580
使用滑动窗口思想,始终保持words 集合中的单词在窗口中仅出现一次,变量count记录窗口中包含words集合中单词的个数,当count == words中单词个数时即找到一个符合条件的子串。

需要使用的内存空间:
1 两张HashMap, dict & currMap, 分别保存words中的单词和窗口中包含的单词,key为单词,value是单词出现个数
2 count, 记录窗口中包含words集合中单词的个数
3 left, 滑动窗口的左边界

实现的步骤:
1 遍历words,构建dict
2 以单词长度为步长,遍历字符串,若当前单词在dict中,则加入或者更新currMap, 并进入步骤3,否则清空currMap和count并且更新left到下一个单词处
3 检查当前单词在窗口中的出现次数是否超过额定值,若超过从左边收缩窗口,收缩方法:从当前左边界开始从currMap中删除单词并递减count直到遇到当前单词(也要删除)。
5 检查count是否 等于words集合单词数,若等于将当前窗口左边界加入结果,更新左边界和currMap、count

这里解释下步骤4中的收缩滑块,这是因为当前滑块中有单词的出现次数超过了额定的出现次数,那么就是需要收缩滑块来剔除这个单词,相当于是从滑块的左起点开始寻找该单词,找到之后,将该单词的右端点作为滑块新的左起点,这样就保证了滑块中所有单词都是小于等于额定出现次数,这样也保证了count计数的有效性。

遇到总单词表中不存在的单词的情况,在步骤2中已经说明,清空当前数据之后继续循环,也就是保证了滑块中是不会出现不存在单词表中的单词的。

最后,考虑最外圈循环,如果是从0开始作为滑块的初始起点,那么其实并没有遍历字符串中的所有可能子串,因为步长是单词长度,所以移动滑块的时候会跨过很多可能子串,所以要在外圈再加一层循环,这个循环的作用就是移动滑块的初始起点,所以循环次数就是单词的长度。

方法1 没有使用滑动窗口思想,会有重复计算,时间复杂度是O(n * m), n为目标字符串长度,m为words集合大小,因此效率不如方法2,其时间复杂度为O(n)

    // method 2: sliding window
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> result = new ArrayList<Integer>();
        if (s == null || s.length() == 0 || words == null || words.length == 0)
            return result;
        int m = words.length, n = s.length();
        int wordLen = words[0].length();
        HashMap<String, Integer> dict = new HashMap<String, Integer>();
        for (int i = 0; i < m; i++) {
            if (!dict.containsKey(words[i]))
                dict.put(words[i], 1);
            else
                dict.put(words[i], dict.get(words[i]) + 1);
        }
        for (int i = 0; i < wordLen; i++) {
            HashMap<String, Integer> currMap = new HashMap<String, Integer>();
            int count = 0;
            int left = i;
            for (int j = i; j + wordLen <= n; j += wordLen) {
                String currWord = s.substring(j, j + wordLen);
                if (dict.containsKey(currWord)) {
                    if (currMap.containsKey(currWord)) {
                        currMap.put(currWord, currMap.get(currWord) + 1);
                    } else {
                        currMap.put(currWord, 1);
                    }
                    count++;
                    if (currMap.get(currWord) > dict.get(currWord)) {
                        // shrinkage left edge of window
                        while (true) {
                            String tmp = s.substring(left, left + wordLen);
                            currMap.put(tmp, currMap.get(tmp) - 1);
                            left += wordLen;
                            count--;
                            if (tmp.equals(currWord))
                                break;
                        }
                    }
                    // found a valid window
                    if (count == m) {
                        result.add(left);
                        String leftWord = s.substring(left, left + wordLen);
                        currMap.put(leftWord, currMap.get(leftWord) - 1);
                        count--;
                        left += wordLen;
                    }
                } else { // find a word which is not in dict, reset all 
                    currMap.clear();
                    count = 0;
                    left = j + wordLen;
                }
            }
        }
        return result;
    }
    // method 1
    public List<Integer> findSubstring1(String s, String[] words) {
        List<Integer> result = new ArrayList<Integer>();
        if (s == null || s.length() == 0 || words == null || words.length == 0)
            return result;
        int m = words.length, n = s.length();
        int singleLen = words[0].length();
        int targetLen = m * singleLen;
        HashMap<String, Integer> dict = new HashMap<String, Integer>();
        for (int i = 0; i < m; i++) {
            if (!dict.containsKey(words[i]))
                dict.put(words[i], 1);
            else
                dict.put(words[i], dict.get(words[i]) + 1);
        }
        int end = n - targetLen;
        for (int i = 0; i <= end; i++) {
            HashMap<String, Integer> map = new HashMap<String, Integer>(dict);
            int j = i;
            while (j < i + targetLen) {
                String curr = s.substring(j, j + singleLen);
                Integer val = map.get(curr);
                if ( val != null && val > 0) {
                    map.put(curr, val - 1);
                    j += singleLen;
                } else {
                    break;
                }
            }
            if (j == i + targetLen)
                result.add(i);
        }
        return result;
    }

你可能感兴趣的:(substring)