题目:30. Substring with Concatenation of All Words 串联所有单词的子串
难度:困难
You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.
Example 1:
Input:
s = "barfoothefoobarman",
words = ["foo","bar"]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are "barfoor" and "foobar" respectively.
The output order does not matter, returning [9,0] is fine too.
Example 2:
Input:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
Output: []
题意解析:
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
解题思路一:
由于数组中的每个元素的长度都是一样的,那么我们可以在字符串中截取长度和数组中所有字符串长度一样的字符串,然后将这个字符串分割成一个数组,将两个数组进行排序后比较是否相等即可。
public List findSubstring(String s, String[] words) {
int wslen = words.length;
int len = s.length();
int i = 0;
List list = new ArrayList<>();
if (wslen == 0 || s.length()==0){
return list;
}
int wlen = words[0].length();
while (i < len && (i+wlen*wslen) <= s.length()){
String s1 = s.substring(i,i+wlen*wslen);
boolean b = check(s1, words);
if (b)
list.add(i);
i++;
}
return list;
}
public boolean check(String s, String[] words){
int len = words[0].length();
int j = 1;
int k = 0;
String[] s1 = new String[words.length];
for (int i = 0; i < s.length(); i++) {
if (j % len == 0){
s1[k++] = s.substring(i-len+1, i+1);
}
j++;
}
Arrays.sort(s1);
Arrays.sort(words);
boolean bool = true;
for (int i = 0; i < words.length; i++) {
if (!words[i].equals(s1[i])){
return false;
}
}
return true;
}
提交代码之后:
Runtime: 466 ms, faster than 8.54% of Java online submissions for Substring with Concatenation of All Words.
Memory Usage: 46.9 MB, less than 9.53% of Java online submissions for Substring with Concatenation of All Words.
解题思路二:
先将数组放进一个map中,其中数组的每一项为key,然后value为它出现的次数,比如,出现了两个word单词,那么最后的map中存放的word所对应的value值就是2.然后对字符串进行遍历操作,每一次都依次取出数组对应个数的字符串,将它存进一个新的map集合中,获取两个map集合的key所对应的value值,如果第二个map大于第一个那么就直接退出循环,或者说第一个map中不包含第二个中的key也退出循环,最后如果得到的j和数组的长度值相等说明循环是顺利执行完了的,就将这个i的值存进list中即可。
public List findSubstring(String s, String[] words) {
List list = new ArrayList<>();
if (s.length() == 0 || words.length==0){
return list;
}
Map map = new HashMap<>();
for (String s1 : words){
map.put(s1, map.getOrDefault(s1, 0) + 1);
}
int n = s.length(), wlen = words.length, len = words[0].length();
for (int i=0; i < n - wlen * len+1; i++){
Map map1 = new HashMap();
int j = 0;
while (j < wlen){
String s1 = s.substring(i+j*len, i+(j+1)*len);
map1.put(s1, map1.getOrDefault(s1, 0) + 1);
if (map.containsKey(s1)){
if (map1.get(s1) > map.get(s1)){
break;
}
}else {
break;
}
j++;
}
if (j == wlen){
list.add(i);
}
}
return list;
}
提交代码之后:
Runtime: 72 ms, faster than 66.99% of Java online submissions for Substring with Concatenation of All Words.
Memory Usage: 38.9 MB, less than 91.15% of Java online submissions for Substring with Concatenation of All Words.
比之上面的算法有了很大的速度提升,但是也并不是最好的解决方案。