zoukankan      html  css  js  c++  java
  • (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 all starting indices of substring(s) in sthat is a concatenation of each word in words exactly once and without any intervening characters.

    Example 1:

    Input:
      s = "barfoothefoobarman",
      words = ["foo","bar"]
    Output: [0,9]
    Explanation: Substrings starting at index 0 and 9 are "barfoor" and "foobar" respectively.
    The output order does not matter, returning [9,0] is fine too.  

    Example 2:

    Input:
      s = "wordgoodstudentgoodword",
      words = ["word","student"]
    Output: []
    

    一道字符串匹配问题。虽然标记是Hard,但想要做出来并不难,因为思路很直观。首先把字典里的单词放到HashMap<String, Integer>中,其中Integer部分存储此单词在字典中出现的次数。遍历字符串的每一个位置,如果是以字典中的某一个单词为前缀,那么继续检查前缀后面剩下的字符串,同时把字典中对应的出现次数减一。当字典中单词出现次数为零的时候证明这个单词已经匹配完毕,那就从字典中删掉。如果字典空了代表所有单词已经全都被匹配,证明这个位置起始的字符串是符合条件的,那么就把起始位置加入结果,之后继续迭代下一个位置。暴力解法一,思路很清晰,代码实现起来就用递归去减少代码量。简洁,清晰,只是…只击败了可怜的5%…

    看了排名第一的解法,茅塞顿开。其中利用到了“滑动窗口”的思想,也有点像KMP。思路是假设字典中每个单词的长度为size,那么以size为窗口大小来滑动窗口。如果有单词加入,就扩大窗口右边界,如果单词不连续,就缩小窗口左边界。这样做的好处是,同一step下的所有满足条件的答案都可以一次性的加到结果中,并且是以size作为step扫描的,效率大大提高。大概的思路是,和解法一一样需要把字典放到HashMap里面,以字典中单词长度size为step,依次扫描字符串对应的位置,比如例一,分别扫描1, 4, 7.../2, 5, 8.../3, 6, 9...。维持一个初始大小为size的窗口,左边界为每次扫描字符串的起始位置,右边界根据每次扫描到的子串单词决定。建立一个新的HashMap记录当前窗口里的单词。如果接下来的子串单词在字典中,那么把他加入到窗口字典。如果窗口字典中单词个数恰好是字典中单词总数,且每个单词对应的出现次数完全一致,那么这就是一个符合要求的匹配。如果扫描到的子串不在字典中,或者窗口字典中单词出现次数已经超过了原本字典单词的出现次数,那就要调整左边界和窗口字典,使匹配能继续进行。详见下文代码。


    解法一(Java)

    class Solution {   
        public List<Integer> findSubstring(String s, String[] words) {
            if (s.length() == 0 || words == null || words.length == 0) return new ArrayList<>();
            List<Integer> res = new ArrayList<>();
            int size = words[0].length(), len = words.length;
            for (int i = 0; i <= s.length() - len; i++) {
                HashMap<String, Integer> m = new HashMap<>();
                for (int j = 0; j < words.length; j++) 
                    m.put(words[j], m.getOrDefault(words[j], 0) + 1);
                if(check(s, i, m, size)) res.add(i);
            }
            return res;
        }
        
        private boolean check(String s, int i, HashMap<String, Integer> m, int size) {
            if (m.size() == 0) return true;
            if (i > s.length() || i + size > s.length()) return false;
            String prefix = s.substring(i, i+size);
            if (m.containsKey(prefix) && m.get(prefix) > 0) {
                m.put(prefix, m.get(prefix)-1);
                if (m.get(prefix) == 0) m.remove(prefix);
                return check(s, i+size, m, size);
            }
            else return false;
        }
    }

    解法二(Java)

    class Solution {
        public List<Integer> findSubstring(String s, String[] words) {
            if (s.length() == 0 || words == null || words.length == 0) return new ArrayList<>();
            List<Integer> res = new ArrayList<>();
            int size = words[0].length(), len = words.length;
            HashMap<String, Integer> m = new HashMap<>();
            HashMap<String, Integer> curM = new HashMap<>();
            for (int j = 0; j < words.length; j++) 
                m.put(words[j], m.getOrDefault(words[j], 0) + 1);
            for (int i = 0; i < size; i++) {
                int start = i;
                int count = 0;
                curM.clear();
                for (int j = i; j <= s.length() - size; j += size) {            
                    String cur = s.substring(j, j + size);
                    if (m.containsKey(cur)) {
                        curM.put(cur, curM.getOrDefault(cur, 0) + 1);
                        count++;
                        while (curM.get(cur) > m.get(cur)) {
                            String left = s.substring(start, start + size);
                            curM.put(left, curM.get(left) - 1);
                            count--;
                            start += size;
                        }
                        if (count == len) {
                            res.add(start);
                            String left = s.substring(start, start + size);
                            curM.put(left, curM.get(left) - 1);
                            count--;
                            start += size;
                        }
                    }
                    else {
                        start = j + size;  
                        curM.clear();
                        count = 0;
                    }
                }
            }
            return res;
        }
    }
  • 相关阅读:
    JAVA List对象与json串互相转换
    git错误提示:fatal: remote origin already exists.
    eclipse中js中ctrl+鼠标点击方法出现 the resource is not on the include path of a javaScript project
    JS根据日期获取这周的周一
    jquery基本操作
    redux+react-redux+示例的快速上手体验
    用 async/await 来处理异步
    Git使用详细教程
    SVN里直接把本地目录纳入管理
    修改npm安装的全局路径和配置环境变量的坑
  • 原文地址:https://www.cnblogs.com/tengdai/p/9307936.html
Copyright © 2011-2022 走看看