题目:https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/
代码:
class Solution { public List<Integer> findSubstring(String s, String[] words) { List<Integer> res = new ArrayList<>(); //每个单词出现的次数,即词频 Map<String, Integer> wordsMap = new HashMap<>(); //子串长度 int subLen = 0; //统计词频和子串长度 for (String w : words) { subLen += w.length(); if (wordsMap.containsKey(w)) { wordsMap.put(w, wordsMap.get(w) + 1); }else{ wordsMap.put(w, 1); } } //滑块遍历 for (int i = 0; i <= s.length() - subLen; i++) { String sub = s.substring(i, i+subLen); if (this.matching(sub, wordsMap)) { res.add(i); } } return res; } /** * 匹配函数 * @param sub * @param wordsMap * @return */ private boolean matching(String sub, Map<String, Integer> wordsMap) { int len = 0; //字串的词频map Map<String, Integer> subMap= new HashMap<>(); for (String word : wordsMap.keySet()) { subMap.put(word, 0); len = word.length(); } //每个单词长度一定,拆分字符串s for (int i = 0; i < sub.length()/len; i++) { String word = sub.substring(i * len, (i+1) * len); if (wordsMap.get(word) == null) { //单词串中没有这个单词,不匹配 return false; } else if (subMap.get(word)+1 > wordsMap.get(word)) { //word的词频已经大于要求,不匹配 return false; } subMap.put(word,subMap.get(word) + 1); } return true; } }
因为子串要与 words 中的单词完全匹配,所以字串的长度是固定的,即words中所有单词的长度之和,那么我们可以考虑是用滑块的形式,如图:
s是字符串,方框部分就是我们所说的滑块,他的长度是固定的,我们通过i的递增即可实现滑块从左滑到右,保证能遍历出s的所有子串sub。那么接下来的问题就是如何判断sub和我们的words中的所有单词是否匹配,即代码中的matching方法。
我们这里使用的是词频对比的方法,恰好题目规定,words中的所有单词长度是一样的,所以针对子串sub,我们可以按照长度进行拆分成一连串的单词,之后遍历这些单词,和要求的词频map(这里的map指的是words数组中的词频,可以提前计算得出)进行比较。因为子串的长度和单词的长度都是固定的,所以想要子串和words能匹配上,则子串拆分出来的单词词频subMap和words的词频wordsMap应该是完全一致的。由此即可实现匹配方法。