SubstringWithConcatenationOfAllWords

串联所有单词的子串

题目介绍

串联所有单词的子串

给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。

示例 1:

1
2
3
4
5
6
7
输入:
s = "barfoothefoobarman",
words = ["foo","bar"]
输出:[0,9]
解释:
从索引 09 开始的子串分别是 "barfoo""foobar"
输出的顺序不重要, [9,0] 也是有效答案。

示例 2:

1
2
3
4
输入:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
输出:[]

题目解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package algorithm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SubstringWithConcatenationOfAllWords {

public static List<Integer> findSubstring(String s, String[] words) {

List<Integer> res = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) {
return res;
}
Map<String, Integer> map = new HashMap<>();
int one_word = words[0].length();
int word_num = words.length;
int all_len = one_word * word_num;
// 统计单词出现次数
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
// 母串从0到可以容纳的长度,循环匹配
for (int i = 0; i < s.length() - all_len + 1; i++) {
String tmp = s.substring(i, i + all_len);
Map<String, Integer> tmp_map = new HashMap<>();
// 每次截取字符串,最后判断是否相等
for (int j = 0; j < all_len; j += one_word) {
String w = tmp.substring(j, j + one_word);
tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
}
if (map.equals(tmp_map)) {
res.add(i);
}
}
return res;
}

public List<Integer> findSubstring1(String s, String[] words) {
List<Integer> res = new ArrayList<>();
Map<String, Integer> wordsMap = new HashMap<>();
if (s.length() == 0 || words.length == 0) return res;
for (String word: words) {
// 主串s中没有这个单词,直接返回空
if (s.indexOf(word) < 0) return res;
// map中保存每个单词,和它出现的次数
wordsMap.put(word, wordsMap.getOrDefault(word, 0) + 1);
}
// 每个单词的长度, 总长度
int oneLen = words[0].length(), wordsLen = oneLen * words.length;
// 主串s长度小于单词总和,返回空
if (wordsLen > s.length()) return res;
// 只讨论从0,1,..., oneLen-1 开始的子串情况,
// 每次进行匹配的窗口大小为 wordsLen,每次后移一个单词长度,由左右窗口维持当前窗口位置
for (int i = 0; i < oneLen; ++i) {
// 左右窗口
int left = i, right = i, count = 0;
// 统计每个符合要求的word
Map<String, Integer> subMap = new HashMap<>();
// 右窗口不能超出主串长度
while (right + oneLen <= s.length()) {
// 得到一个单词
String word = s.substring(right, right + oneLen);
// 有窗口右移
right += oneLen;
// words[]中没有这个单词,那么当前窗口肯定匹配失败,直接右移到这个单词后面
if (!wordsMap.containsKey(word)) {
left = right;
// 窗口内单词统计map清空,重新统计
subMap.clear();
// 符合要求的单词数清0
count = 0;
} else {
// 统计当前子串中这个单词出现的次数
subMap.put(word, subMap.getOrDefault(word, 0) + 1);
++count;
// 如果这个单词出现的次数大于words[]中它对应的次数,又由于每次匹配和words长度相等的子串
// 如 "foobarfoobarfoothe" ["foo","bar","foo","the"]
// 第二个bar虽然是words[]中的单词,但是次数抄了,那么右移一个单词长度后 "barfoobarfoothe"
// bar还是不符合,所以直接从这个不符合的bar之后开始匹配,也就是将这个不符合的bar和它之前的单词(串)全移出去
while (subMap.getOrDefault(word, 0) > wordsMap.getOrDefault(word, 0)) {
// 从当前窗口字符统计map中删除从左窗口开始到数量超限的所有单词(次数减一)
String w = s.substring(left, left + oneLen);
subMap.put(w, subMap.getOrDefault(w, 0) - 1);
// 符合的单词数减一
--count;
// 左窗口位置右移
left += oneLen;
}
// 当前窗口字符串满足要求
if (count == words.length) res.add(left);
}
}
}
return res;
}

public static void main(String[] args) {
String s = "aaaaaaaaaaaaaa";
String[] words = new String[]{"aa", "aa"};
System.out.println(findSubstring(s, words));

s = "wordgoodgoodgoodbestword";
words = new String[]{"word", "good", "best", "word"};
System.out.println(findSubstring(s, words));

s = "barfoofoobarthefoobarman";
words = new String[]{"bar", "foo", "the"};
System.out.println(findSubstring(s, words));
}
}

打印:

1

思路:

这道题没做出来。本来以为是字符匹配。应该好好审题,子串长度都是一致的。算法大致有两种,hash存储子串和个数,最终比较;另一种,与之类似,只不过,用滑动窗口实现优化。


SubstringWithConcatenationOfAllWords
https://yangtzeshore.github.io/2021/02/02/SubstringWithConcatenationOfAllWords/
作者
Chen Peng
发布于
2021年2月2日
许可协议