Skip to content

Commit 7278832

Browse files
author
wangpeng
committed
feat(HARD): add _30_findSubstring
1 parent 8e713b1 commit 7278832

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package pp.arithmetic.leetcode;
2+
3+
import pp.arithmetic.Util;
4+
5+
import java.util.ArrayList;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
/**
11+
* Created by wangpeng on 2019-04-17.
12+
* 30. 串联所有单词的子串
13+
* <p>
14+
* 给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
15+
* <p>
16+
* 注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
17+
* <p>
18+
* <p>
19+
* <p>
20+
* 示例 1:
21+
* <p>
22+
* 输入:
23+
* s = "barfoothefoobarman",
24+
* words = ["foo","bar"]
25+
* 输出:[0,9]
26+
* 解释:
27+
* 从索引 0 和 9 开始的子串分别是 "barfoor" 和 "foobar" 。
28+
* 输出的顺序不重要, [9,0] 也是有效答案。
29+
* 示例 2:
30+
* <p>
31+
* 输入:
32+
* s = "wordgoodgoodgoodbestword",
33+
* words = ["word","good","best","word"]
34+
* 输出:[]
35+
*
36+
* @see <a href="https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/">substring-with-concatenation-of-all-words</a>
37+
*/
38+
public class _30_findSubstring {
39+
40+
public static void main(String[] args) {
41+
_30_findSubstring findSubstring = new _30_findSubstring();
42+
List<Integer> list = findSubstring.findSubstring("barfoothefoobarman", new String[]{"foo", "bar"});
43+
Util.printList(list);
44+
List<Integer> list1 = findSubstring.findSubstring2("wordgoodgoodgoodbestword", new String[]{"word", "good", "best", "good"});
45+
Util.printList(list1);
46+
}
47+
48+
/**
49+
* 解题思路:
50+
* 这是一道字符串中找单词的问题,需要找到单词的位置,不可能每次都循环遍历,由于是字符串的问题可以考虑用哈希表
51+
* 1、遍历单词,哈希表储存每个单词出现的次数
52+
* 2、遍历字符串,按单词长度跳跃,找到每个单词出现的次数
53+
* 3、次数超过对于单词或者未找到相应匹配,则跳过
54+
* <p>
55+
* 执行用时 : 242 ms, 在Substring with Concatenation of All Words的Java提交中击败了45.27% 的用户
56+
* 内存消耗 : 63.2 MB, 在Substring with Concatenation of All Words的Java提交中击败了33.24% 的用户
57+
* <p>
58+
* 通过提交leetcode来看,效率并不是很高,分析下时间复杂度在O(n^2)
59+
* <p>
60+
* 优化方案 {@link _30_findSubstring#findSubstring2(String, String[])}
61+
*
62+
* @param s
63+
* @param words
64+
* @return
65+
*/
66+
public List<Integer> findSubstring(String s, String[] words) {
67+
List<Integer> retList = new ArrayList<>();
68+
if (words.length == 0) {
69+
return retList;
70+
}
71+
int wordLen = words[0].length();
72+
HashMap<String, Integer> map = new HashMap<>();
73+
for (int i = 0; i < words.length; i++) {
74+
map.put(words[i], map.getOrDefault(words[i], 0) + 1);
75+
}
76+
int si = 0, ei;
77+
HashMap<String, Integer> iteMap = new HashMap<>();
78+
int forLen = s.length() - words.length * wordLen;
79+
while (si <= forLen) {
80+
ei = si;
81+
while (ei <= s.length() - wordLen) {
82+
String item = s.substring(ei, ei + wordLen);
83+
if (map.getOrDefault(item, 0) == 0) {
84+
si++;
85+
iteMap.clear();
86+
break;
87+
}
88+
//遍历次数
89+
int iteCount = iteMap.getOrDefault(item, 0);
90+
iteMap.put(item, ++iteCount);
91+
if (iteCount > map.get(item)) {
92+
//出现次数已超过
93+
si++;
94+
iteMap.clear();
95+
break;
96+
}
97+
ei += wordLen;
98+
if (ei - si == words.length * wordLen) {
99+
//找到满足条件的
100+
retList.add(si);
101+
si++;
102+
iteMap.clear();
103+
break;
104+
}
105+
}
106+
}
107+
108+
return retList;
109+
}
110+
111+
112+
/**
113+
* 优化方案二:leetcode解题
114+
*
115+
* 执行用时 : 27 ms, 在Substring with Concatenation of All Words的Java提交中击败了93.31% 的用户
116+
* 内存消耗 : 44.8 MB, 在Substring with Concatenation of All Words的Java提交中击败了78.98% 的用户
117+
*
118+
* 大体思路和方案一一致,优化的点是减少了无效的循环次数fori,重复利用了forj不满足条件后的平移
119+
*
120+
*
121+
* @param s
122+
* @param words
123+
* @return
124+
*/
125+
public List<Integer> findSubstring2(String s, String[] words) {
126+
if (words.length == 0)
127+
return new ArrayList<>();
128+
int num = 0;
129+
List<Integer> res = new ArrayList<>();
130+
Map<String, Integer> wordsCount = new HashMap<>();
131+
Map<String, Integer> usedWords = new HashMap<>();
132+
for (String w : words)
133+
wordsCount.put(w, wordsCount.getOrDefault(w, 0) + 1);
134+
int wlen = words[0].length();
135+
for (int i = 0; i < wlen; i++, num = 0, usedWords.clear()) {
136+
for (int j = i; j + wlen <= s.length(); j += wlen) {
137+
String sub = s.substring(j, j + wlen);
138+
if (wordsCount.containsKey(sub)) {
139+
num++;
140+
usedWords.put(sub, usedWords.getOrDefault(sub, 0) + 1);
141+
while (usedWords.get(sub) > wordsCount.get(sub)) {
142+
String rem = s.substring(j - (num - 1) * wlen, j - (num - 2) * wlen);
143+
usedWords.put(rem, usedWords.get(rem) - 1);
144+
num--;
145+
}
146+
} else {
147+
num = 0;
148+
usedWords.clear();
149+
}
150+
if (num == words.length) {
151+
res.add(j - (num - 1) * wlen);
152+
}
153+
}
154+
}
155+
return res;
156+
}
157+
}

0 commit comments

Comments
 (0)