给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"] | |
输出:[0,9] | |
解释: | |
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。 | |
输出的顺序不重要, [9,0] 也是有效答案。 |
示例 2:
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"] | |
输出:[] |
示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"] | |
输出:[6,9,12] |
提示:
- 1 <= s.length <= 104
- s 由小写英文字母组成
- 1 <= words.length <= 5000
- 1 <= words[i].length <= 30
- words [i] 由小写英文字母组成
题解一
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
/** | |
* s = "barfoofoobarthefoobarman", words = ["bar","foo","the"] | |
* 思路: | |
* 单词长度为 3 | |
* 拷贝一份新的 newWords = words | |
* 从下标 0 进行递归 | |
* 找到第一个单词在 newWords 里面的,将该单词在 newWords 移除掉,并进行下次递归,直到 newWords 没值,记录该下标。 | |
* 下标 + 1 | |
* 该解法会超时 | |
* | |
*/ | |
public class Soultion1 { | |
public List<Integer> findSubstring(String s, String[] words) { | |
// 每个单词的长度 | |
int wordLength = words[0].length(); | |
// 记录满足的下标值 | |
List<Integer> result = new ArrayList<>(); | |
for (int i = 0; i < s.length(); i++){ | |
// 拷贝一份新的出来,执行删除操作时不影响原数据 | |
List<String> newWords = new ArrayList<>(Arrays.asList(words)); | |
// 如果有效,记录下标值 | |
if (effective(s, i, wordLength, newWords)){ | |
result.add(i); | |
} | |
} | |
return result; | |
} | |
private boolean effective(String s, int pos, int wordLength, List<String> words) { | |
// 当 words 为空时,说明该下标满足条件 | |
if (words.size() == 0) return true; | |
// 如果当前下标 + 单词长度超出了总数据长度,则不满足条件 | |
if (pos + wordLength > s.length()) return false; | |
// 当前单词 | |
String nowWord = s.substring(pos, pos + wordLength); | |
// 如果当前单词在 words 里面,进行递归,判断下一个单词是否在剩下的 words 里面 | |
if (contains(nowWord, words)){ | |
return effective(s, pos + wordLength, wordLength, words); | |
}else { | |
return false; | |
} | |
} | |
private boolean contains(String nowWord, List<String> words) { | |
for (int i = 0; i < words.size(); i++){ | |
// 如果在 words 里面,移除该元素 | |
if (nowWord.equals(words.get(i))){ | |
words.remove(i); | |
return true; | |
} | |
} | |
return false; | |
} | |
} |
题解二
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.List; | |
/** | |
* s = "barfoofoobarthefoobarman", words = ["bar","foo","the"] | |
* 思路: | |
* 从下标 0 开始,统计第一轮单词组 sList = ["bar","foo","foo"] | |
* 如果 sList 与 words 内容相同,记录该下标 | |
* 下标 + 1 | |
* 该解法刚好通过 | |
*/ | |
public class Soultion2 { | |
public List<Integer> findSubstring(String s, String[] words) { | |
// 每个单词的长度 | |
int wordLength = words[0].length(); | |
// 记录满足的下标值 | |
List<Integer> result = new ArrayList<>(); | |
for (int i = 0; i < s.length(); i++){ | |
// 拷贝一份新的出来,执行删除操作时不影响原数据 | |
List<String> newWords = new ArrayList<>(Arrays.asList(words)); | |
// 如果有效,记录下标值 | |
if (effective(s, i, wordLength, newWords)){ | |
result.add(i); | |
} | |
} | |
return result; | |
} | |
private boolean effective(String s, int pos, int wordLength, List<String> words) { | |
// 如果当前下标 + words 里面单词所有的长度超过数据长度,则不满足要求 | |
if (pos + words.size() * wordLength > s.length()) return false; | |
// 记录 pos 到 pos + words.size () * wordLength 的数据 | |
List<String> sList = new ArrayList<>(); | |
for (int i = 0; i < words.size(); i++){ | |
sList.add(s.substring(pos + i * wordLength, pos + (i + 1) * wordLength)); | |
} | |
// 将当前截取长度进行排序 | |
Collections.sort(sList); | |
// 将 words 进行排序 | |
Collections.sort(words); | |
// 如果两者相等,则满足要求 | |
if (sList.equals(words)){ | |
return true; | |
}else { | |
return false; | |
} | |
} | |
} |
题解三
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
/** | |
* s = "barfoofoobarthefoobarman", words = ["bar","foo","the"] | |
* 思路: | |
* 第一轮单词为 bar,foo,foo | |
* 将 words 转为 map = {"bar": 1,"foo": 1,"the": 1} | |
* 如果当前单词在 map 中,数量减一,如果数量为零,则将该单词在 map 中移除掉 | |
* 如果 map 中无单词,说明当前下标满足要求 | |
* 下标 + 1 | |
* 该解法要比上面快一点点 | |
*/ | |
public class Soultion3 { | |
public List<Integer> findSubstring(String s, String[] words) { | |
// 每个单词的长度 | |
int wordLength = words[0].length(); | |
// 记录满足的下标值 | |
List<Integer> result = new ArrayList<>(); | |
// 将数组转换成 map 的形式 | |
Map<String, Integer> mapWords = convertToMap(words); | |
for (int i = 0; i < s.length(); i++){ | |
// 拷贝一份新的出来,执行删除操作时不影响原数据 | |
Map<String, Integer> newWords = new HashMap<>(mapWords); | |
// 如果有效,记录下标值 | |
if (effective(s, i, wordLength, newWords, words.length)){ | |
result.add(i); | |
} | |
} | |
return result; | |
} | |
private Map<String, Integer> convertToMap(String[] words) { | |
Map<String, Integer> map = new HashMap<>(); | |
for (int i = 0; i < words.length; i++){ | |
if (map.containsKey(words[i])){ | |
map.put(words[i], map.get(words[i]) + 1); | |
}else { | |
map.put(words[i], 1); | |
} | |
} | |
return map; | |
} | |
private boolean effective(String s, int pos, int wordLength, Map<String, Integer> words, int length) { | |
// 如果当前下标 + words 里面单词所有的长度超过数据长度,则不满足要求 | |
if (pos + length * wordLength > s.length()) return false; | |
for (int i = 0; i < length; i++){ | |
// 当前单词 | |
String substring = s.substring(pos + i * wordLength, pos + (i + 1) * wordLength); | |
// 如果当前单词在 words 中,并且数量大于 1,将数量减 1 | |
// 否则将该单词在 words 中移除 | |
if (words.containsKey(substring)){ | |
Integer number = words.get(substring); | |
if (number - 1 == 0) words.remove(substring); | |
else words.put(substring, number - 1); | |
}else { | |
return false; | |
} | |
} | |
// 如果 words 中单词都不存在,则说明满足要求 | |
return words.isEmpty(); | |
} | |
} |
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/substring-with-concatenation-of-all-words