|
| 1 | +## 与所有单词相关联的字串 |
| 2 | +### 题目描述 |
| 3 | +给定一个字符串 s 和一些长度相同的单词 words。在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置。 |
| 4 | + |
| 5 | +注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。 |
| 6 | + |
| 7 | +``` |
| 8 | +示例 1: |
| 9 | +
|
| 10 | +输入: |
| 11 | + s = "barfoothefoobarman", |
| 12 | + words = ["foo","bar"] |
| 13 | +输出: [0,9] |
| 14 | +解释: 从索引 0 和 9 开始的子串分别是 "barfoor" 和 "foobar" 。 |
| 15 | +输出的顺序不重要, [9,0] 也是有效答案。 |
| 16 | +示例 2: |
| 17 | +
|
| 18 | +输入: |
| 19 | + s = "wordgoodstudentgoodword", |
| 20 | + words = ["word","good","good"] |
| 21 | + (ps:原题的例子为 words = ["word","student"] 和题目描述不符,这里私自改了一下) |
| 22 | +输出: [] |
| 23 | +``` |
| 24 | + |
| 25 | +### 解法 |
| 26 | +1. 用 HashMap< 单词, 出现次数 > map 来存储所有单词; |
| 27 | +2. 设单词数量为 N ,每个单词长度为 len,则我们只需要对比到 **str.length() - N \* len** , |
| 28 | +再往后因为不足 N \* len 个字母,肯定不匹配; |
| 29 | +3. 每次从 str 中选取连续的 N \* len 个字母进行匹配时,**从后向前匹配**,因为若后面的单词不匹配, |
| 30 | +无论前面的单词是否匹配,当前选取的字串一定不匹配,且,最后一个匹配的单词前的部分一定不在匹配的字串中, |
| 31 | +这样下一次选取长度为 N \* len 的字串时,可以**从上次匹配比较中最后一个匹配的单词开始**,减少了比较的次数; |
| 32 | +4. 考虑到要点 3 中对前一次匹配结果的利用,遍历 str 时,采用间隔为 len 的形式。 |
| 33 | +例如示例 1 ,遍历顺序为:(0 3 6 9 12 15) (1 4 7 10 13)(2 5 8 11 14) |
| 34 | + |
| 35 | + |
| 36 | +```java |
| 37 | +class Solution { |
| 38 | + public List<Integer> findSubstring(String s, String[] words) { |
| 39 | + |
| 40 | + List<Integer> re = new ArrayList<>(); |
| 41 | + |
| 42 | + if(s == null || words == null || words.length == 0 || words[0] == null) { |
| 43 | + return re; |
| 44 | + } |
| 45 | + if(s.length() == 0 || words[0].length() == 0 || s.length() < words.length * words[0].length()) { |
| 46 | + return re; |
| 47 | + } |
| 48 | + // 用< 单词,出现次数 > 来存储 words 中的元素,方便查找 |
| 49 | + HashMap<String,Integer> map = new HashMap(); |
| 50 | + for (String string : words) { |
| 51 | + map.put(string, map.getOrDefault(string,0) + 1); |
| 52 | + } |
| 53 | + int len = words[0].length(); |
| 54 | + int strLen = s.length(); |
| 55 | + int lastStart = len * words.length - len; |
| 56 | + |
| 57 | + for (int i = 0; i < len; i++) { |
| 58 | + for (int j = i; j <= strLen - len - lastStart; j += len) { |
| 59 | + String tempStr = s.substring(j, j + len); |
| 60 | + if(map.containsKey(tempStr)) { |
| 61 | + HashMap<String,Integer> searched = new HashMap<>(); |
| 62 | + // 从后向前依次对比 |
| 63 | + int tempIndex = j + lastStart; |
| 64 | + String matchedStr = s.substring(tempIndex, tempIndex + len); |
| 65 | + while (tempIndex >= j && map.containsKey(matchedStr)) { |
| 66 | + // 正确匹配到单词 |
| 67 | + if(searched.getOrDefault(matchedStr,0) < map.get(matchedStr)) { |
| 68 | + searched.put(matchedStr, searched.getOrDefault(matchedStr,0) + 1); |
| 69 | + } |
| 70 | + else { |
| 71 | + break; |
| 72 | + } |
| 73 | + tempIndex -= len; |
| 74 | + if(tempIndex < j) { |
| 75 | + break; |
| 76 | + } |
| 77 | + matchedStr = s.substring(tempIndex, tempIndex + len); |
| 78 | + } |
| 79 | + // 完全匹配所以单词 |
| 80 | + if(j > tempIndex) { |
| 81 | + re.add(j); |
| 82 | + } |
| 83 | + // 从tempIndex 到 tempIndex + len 这个单词不能正确匹配 |
| 84 | + else { |
| 85 | + j = tempIndex; |
| 86 | + } |
| 87 | + } |
| 88 | + } |
| 89 | + } |
| 90 | + return re; |
| 91 | + } |
| 92 | +} |
| 93 | +``` |
0 commit comments