leetcode 30. 串联所有单词的子串-java详细版本

题目所属分类

可以用字符串哈希做 这样的话复杂度会变成O(n)

原题链接

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。

s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。

例如,如果 words = [“ab”,“cd”,“ef”], 那么 “abcdef”, “abefcd”,“cdabef”, “cdefab”,“efabcd”, 和 “efcdab” 都是串联子串。 “acdbef” 不是串联子串,因为他不是任何 words 排列的连接。
返回所有串联字串在 s 中的开始索引。你可以以 任意顺序 返回答案。

代码案例:输入:s = “barfoothefoobarman”, words = [“foo”,“bar”]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 “barfoo” 开始位置是 0。它是 words 中以 [“bar”,“foo”] 顺序排列的连接。
子串 “foobar” 开始位置是 9。它是 words 中以 [“foo”,“bar”] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

题解

  1. (重点)划分区间,因为每个word的长度是相同的,因此可以将s串划分为从第0个元素开始匹配、从第1个元素开始匹配、… 、从第w-1个元素开始匹配的w种情况,这样就能保证给定的words串不会横跨区间。
    leetcode 30. 串联所有单词的子串-java详细版本_第1张图片
  2. 在上述基础上,用一个哈希表a存储给定的m个单词;用另一个哈希表b存储滑动窗口中的元素,每次滑动窗口时,只会在此哈希表中插入一个元素,再删除一个元素;(键值对为 ---- 单词 :单词出现的次数)
  3. 用cnt存储哈希表b中有多少个单词是哈希表a中的。如果窗口中出现过该单词,并且窗口中该单词的数量小于原单词组中的数量,则表示该单词有效,cnt++。最后,如果cnt = m,则得到一个答案。从而使得滑动窗口匹配子串时的时间复杂度降为O(1)。
    这个写的好一些
    应用了字符串哈希
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        // 存储答案
        List<Integer> list = new ArrayList<Integer>();
        // n为字符串长度,m为单词数量,w为单词长度
        int n = s.length(),m = words.length,w = words[0].length();
        // 将原来的单词存储在哈希表中,value值表示它有多少个
        HashMap<String, Integer> map_words = new HashMap<String, Integer>();
        for(String word : words) map_words.put(word,map_words.getOrDefault(word,0) + 1);
        // 定义当前窗口的哈希表
        HashMap<String, Integer> map_windows = new HashMap<String, Integer>();
        // 划分s串,定义窗口的起始位置从0 到 n - m * w
        for(int i = 0;i <= n - m * w;i ++)
        {
            // 每次读入窗口清空原来的窗口哈希表
            map_windows.clear();
            // cnt表示哈希表map_windows中有多少个单词是哈希表map_words中的
            int cnt = 0;
            // 划分单词m次
            for(int j = 0;j < m;j ++)
            {
                // 将长度为w的单词划分出来
                String t = s.substring(i + j * w,i + j * w + w);
                // 如果原单词中存在t,则继续,否则直接break此次循环
                if(map_words.containsKey(t))
                {
                    // 将t存储在map_windows中
                    map_windows.put(t,map_windows.getOrDefault(t,0) + 1);
                    // 如果t在窗口中出现的次数大于t在原单词组中出现的次数,直接break此次循环
                    if(map_windows.get(t) > map_words.get(t)) break;
                    else cnt++;
                }
                else break;
            }
            if(cnt == m) list.add(i);
        }
        return list;
    }
}

 

字符串哈希没用上

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();      
        if(words.length == 0) return res;
        int n = s.length();int m = words.length ; int w = words[0].length();
         HashMap<String, Integer> total = new HashMap<String, Integer>();
        for(String word : words){
            total.put(word,total.getOrDefault(word,0) + 1);
        }
        for(int i = 0 ; i < w  ;i++){
        // 因为每一个单词的长度是相等的 == w,所以只需要遍历起点 0--w - 1
            //当前窗口的的哈希表
        HashMap<String, Integer> find = new HashMap<String, Integer>();
        int cnt = 0 ;// 统计窗口内单词在 words 中出现的次数
        for(int j = i ; j + w <= n  ; j += w ){
             //窗口已经满,需要去掉窗口最左边的单词,才能在窗口中添加新的单词
            if(j >= i + m * w){
                String word = s.substring(j-m*w, w+j-m*w);
                find.put(word,find.get(word)-1);//去除窗口最左边的单词
                 // 不加 == 是因为没有减去的时候是 == ,减去了之后是 <,那 word 肯定是需要的单词
                 //total.get(word) != null这个不加会报错
                if (total.get(word) != null && find.get(word) < total.get(word))//说明减到的是一个有效单词
                        cnt--;
            }
             String word = s.substring(j, j+w); 
                find.put(word, find.getOrDefault(word, 0) + 1); //在窗口最右边添加新的单词
                if (total.get(word) != null && find.get(word) <= total.get(word))
                    cnt++;
                if (cnt == m)
                    res.add(j - (m-1)*w);//区间的起点
        }

        }
        return res ;
    }
}

总结

  • 划分字符串的技巧
  • 用哈希表存储单词以及单词出现的次数
  • 用cnt来控制两个哈希表是否相同,如果窗口中出现过该单词,并且窗口中该单词的数量小于原单词组中的数量,则表示该单词有效,cnt++。

你可能感兴趣的:(leetcode,java,算法)