30. 串联所有单词的子串(困难)twice

1,问题描述

30. 串联所有单词的子串

难度:困难

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

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

  • 例如,如果 words = ["ab","cd","ef"], 那么 "abcdef""abefcd""cdabef""cdefab""efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。

返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

示例 1:

1
2
3
4
5
6
输入: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] 也是可以的。

示例 2:

1
2
3
4
5
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

示例 3:

1
2
3
4
5
6
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。

提示:

  • 1 <= s.length <= 104
  • 1 <= words.length <= 5000
  • 1 <= words[i].length <= 30
  • words[i]s 由小写英文字母组成

2,初步思考

​ 我自己没有想出解决方案,但是看了官方的题解解决了

3,代码处理

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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import java.util.*;

public class _30串联所有单词的子串 {

// 官方解法
public List<Integer> findSubstring_gov(String s, String[] words) {
List<Integer> res = new ArrayList<Integer>();
int m = words.length, n = words[0].length(), ls = s.length();
for (int i = 0; i < n; i++) {
if (i + m * n > ls) {
break;
}

Map<String, Integer> differ = new HashMap<String, Integer>();
for (int j = 0; j < m; j++) {
String word = s.substring(i + j * n, i + (j + 1) * n);
differ.put(word, differ.getOrDefault(word, 0) + 1);
}
for (String word : words) {
differ.put(word, differ.getOrDefault(word, 0) - 1);
if (differ.get(word) == 0) {
differ.remove(word);
}
}
for (int start = i; start < ls - m * n + 1; start += n) {
if (start != i) {
String word = s.substring(start + (m - 1) * n, start + m * n);
differ.put(word, differ.getOrDefault(word, 0) + 1);
if (differ.get(word) == 0) {
differ.remove(word);
}
word = s.substring(start - n, start);
differ.put(word, differ.getOrDefault(word, 0) - 1);
if (differ.get(word) == 0) {
differ.remove(word);
}
}
if (differ.isEmpty()) {
res.add(start);
}
}
}
return res;
}


// 解法3:滑动窗
public List<Integer> findSubstring_slide_window_v2(String s, String[] words) {
// 记录字典
Map<String, Integer> map = new HashMap<>();
wordLen = words[0].length();
wordsLen = words.length;
wordSumLen = wordsLen * wordLen;
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}

// 遍历字符串
List<Integer> res = new ArrayList<>();
for (int i = 0; i < wordLen; i++) {
nextDeal(res, map, s, i);
}
return res;
}

private void nextDeal(List<Integer> res, Map<String, Integer> map, String s, int index) {
Map<String, Integer> mapCur = new HashMap<>(map);
for (int i = 0; i < wordsLen && index + (i + 1) * wordLen <= s.length(); i++) {// 处理预数据
updateMap(mapCur, s.substring(index + i * wordLen, index + (i + 1) * wordLen), false);
}
nextDealChild(res, mapCur, s, index + wordSumLen);
}

private void nextDealChild(List<Integer> res, Map<String, Integer> map, String s, int index) {
if (map.isEmpty()) {
res.add(index - wordSumLen);
}
if (index + wordLen > s.length()) {
return;
}
String addStr = s.substring(index, index + wordLen);
String deleteStr = s.substring(index - wordSumLen, index - wordSumLen + wordLen);
updateMap(map, addStr, true);
updateMap(map, deleteStr, false);
nextDealChild(res, map, s, index + wordLen);
}


int wordLen = 0;
int wordsLen = 0;
int wordSumLen = 0;

// 解法2:滑动窗,还是有很多重复计算,运行超时
public List<Integer> findSubstring_slide_window(String s, String[] words) {
// 记录字典
Map<String, Integer> map = new HashMap<>();
wordLen = words[0].length();
wordsLen = words.length;
wordSumLen = wordsLen * wordLen;
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}

// 遍历字符串
List<Integer> res = new ArrayList<>();
next(res, map, s, 0, true);
return res;
}

private void next(List<Integer> res, Map<String, Integer> map, String s, Integer index, boolean isFirst) {
if (map.isEmpty()) {// 退出条件
res.add(index - wordSumLen);
return;
}

// 遍历
List<String> keys = new ArrayList<>(map.keySet());
if (isFirst) {
for (int i = index; i <= s.length() - wordSumLen; i++) {
for (String key : keys) {
if (index + wordLen > s.length()) {// 剪枝,超出长度直接返回
continue;
}
if (!Objects.equals(key, s.substring(i, i + wordLen))) {// 不相登直接跳过
continue;
}
updateMap(map, key, false);// 更新字典
next(res, map, s, i + wordLen, false);
updateMap(map, key, true);// 恢复字典
}
}
} else {
for (String key : keys) {
if (index + wordLen > s.length()) {// 剪枝,超出长度直接返回
continue;
}
if (!Objects.equals(key, s.substring(index, index + wordLen))) {// 不相登直接跳过
continue;
}
updateMap(map, key, false);// 更新字典
next(res, map, s, index + wordLen, false);
updateMap(map, key, true);// 恢复字典
}
}
}


private void updateMap(Map<String, Integer> map, String key, boolean isInput) {
if (isInput) {
if (map.containsKey(key)) {
int counter = map.get(key) + 1;
if (counter == 0) {
map.remove(key);
} else {
map.put(key, counter);
}
} else {
map.put(key, 1);
}
} else {
if (map.containsKey(key)) {
int counter = map.get(key) - 1;
if (counter == 0) {
map.remove(key);
} else {
map.put(key, counter);
}
} else {
map.put(key, -1);
}
}
}


// 解法:暴力解法来一波
// 1,取出所有可能的子字符串,然后遍历字符串找出所有的开始索引
public List<Integer> findSubstring(String s, String[] words) {
List<List<String>> lists = permuteUnique_gov(words);
Set<String> set = new HashSet<>();
for (List<String> list : lists) {
StringBuilder sb = new StringBuilder();
for (String string : list) {
sb.append(string);
}
set.add(sb.toString());
}
List<Integer> res = new ArrayList<>();

for (String string : set) {
StringBuilder sb = new StringBuilder(s);
int index = sb.indexOf(string);
while (index != -1) {
res.add(index);
index = sb.indexOf(string, index + 1);
}
}
return res;
}


public List<List<String>> permuteUnique_gov(String[] nums) {
int len = nums.length;
List<List<String>> res = new ArrayList<>();
List<String> output = new ArrayList<>();
Arrays.sort(nums);
for (String num : nums) {
output.add(num);
}
backtrack(len, 0, output, res);
return res;
}

private void backtrack(int len, int index, List<String> output, List<List<String>> res) {
if (index == len) res.add(new ArrayList<>(output));
Set<String> set = new HashSet<>();
for (int i = index; i < len; i++) {
if (set.contains(output.get(i))) {// 已经使用过的数据直接跳过即可
continue;
}
set.add(output.get(i));
Collections.swap(output, index, i);// 交换位置,递归处理
backtrack(len, index + 1, output, res);
Collections.swap(output, index, i);// 恢复原样
}
}

public static void main(String[] args) {
_30串联所有单词的子串 solution = new _30串联所有单词的子串();
// System.out.println(solution.findSubstring_slide_window_v2("barfoothefoobarman", new String[]{"foo", "bar"}));
// System.out.println(solution.findSubstring_slide_window("wordgoodgoodgoodbestword", new String[]{"word", "good", "best", "good"}));
// System.out.println(solution.findSubstring("barfoofoobarthefoobarman", new String[]{"bar", "foo", "the"}));
System.out.println(solution.findSubstring_slide_window_v2("pjzkrkevzztxductzzxmxsvwjkxpvukmfjywwetvfnujhweiybwvvsrfequzkhossmootkmyxgjgfordrpapjuunmqnxxdrqrfgkrsjqbszgiqlcfnrpjlcwdrvbumtotzylshdvccdmsqoadfrpsvnwpizlwszrtyclhgilklydbmfhuywotjmktnwrfvizvnmfvvqfiokkdprznnnjycttprkxpuykhmpchiksyucbmtabiqkisgbhxngmhezrrqvayfsxauampdpxtafniiwfvdufhtwajrbkxtjzqjnfocdhekumttuqwovfjrgulhekcpjszyynadxhnttgmnxkduqmmyhzfnjhducesctufqbumxbamalqudeibljgbspeotkgvddcwgxidaiqcvgwykhbysjzlzfbupkqunuqtraxrlptivshhbihtsigtpipguhbhctcvubnhqipncyxfjebdnjyetnlnvmuxhzsdahkrscewabejifmxombiamxvauuitoltyymsarqcuuoezcbqpdaprxmsrickwpgwpsoplhugbikbkotzrtqkscekkgwjycfnvwfgdzogjzjvpcvixnsqsxacfwndzvrwrycwxrcismdhqapoojegggkocyrdtkzmiekhxoppctytvphjynrhtcvxcobxbcjjivtfjiwmduhzjokkbctweqtigwfhzorjlkpuuliaipbtfldinyetoybvugevwvhhhweejogrghllsouipabfafcxnhukcbtmxzshoyyufjhzadhrelweszbfgwpkzlwxkogyogutscvuhcllphshivnoteztpxsaoaacgxyaztuixhunrowzljqfqrahosheukhahhbiaxqzfmmwcjxountkevsvpbzjnilwpoermxrtlfroqoclexxisrdhvfsindffslyekrzwzqkpeocilatftymodgztjgybtyheqgcpwogdcjlnlesefgvimwbxcbzvaibspdjnrpqtyeilkcspknyylbwndvkffmzuriilxagyerjptbgeqgebiaqnvdubrtxibhvakcyotkfonmseszhczapxdlauexehhaireihxsplgdgmxfvaevrbadbwjbdrkfbbjjkgcztkcbwagtcnrtqryuqixtzhaakjlurnumzyovawrcjiwabuwretmdamfkxrgqgcdgbrdbnugzecbgyxxdqmisaqcyjkqrntxqmdrczxbebemcblftxplafnyoxqimkhcykwamvdsxjezkpgdpvopddptdfbprjustquhlazkjfluxrzopqdstulybnqvyknrchbphcarknnhhovweaqawdyxsqsqahkepluypwrzjegqtdoxfgzdkydeoxvrfhxusrujnmjzqrrlxglcmkiykldbiasnhrjbjekystzilrwkzhontwmehrfsrzfaqrbbxncphbzuuxeteshyrveamjsfiaharkcqxefghgceeixkdgkuboupxnwhnfigpkwnqdvzlydpidcljmflbccarbiegsmweklwngvygbqpescpeichmfidgsjmkvkofvkuehsmkkbocgejoiqcnafvuokelwuqsgkyoekaroptuvekfvmtxtqshcwsztkrzwrpabqrrhnlerxjojemcxel", new String[]{"dhvf", "sind", "ffsl", "yekr", "zwzq", "kpeo", "cila", "tfty", "modg", "ztjg", "ybty", "heqg", "cpwo", "gdcj", "lnle", "sefg", "vimw", "bxcb"}));
}
}