- 题目描述
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
来源:LeetCode
- 示例
-
示例 1:
输入: s = “barfoothefoobarman”, words = [“foo”,“bar”]
输出:[0,9]
解释:从索引 0 和 9 开始的子串分别是 “barfoo” 和 “foobar” 。输出的顺序不重要, [9,0] 也是有效答案。 -
示例 2:
输入:s = “wordgoodgoodgoodbestword”, words = [“word”,“good”,“best”,“word”]
输出:[]
- 思路分析
这道题比较难的地方在单词串联顺序随意,因此需要判断这个每个单词是否出现过以及出现的次数。我想到的是使用哈希表,可以巧妙地避免考虑单词顺序的问题。
- 首先用哈希表记录words中各单词及各单词的出现次数,然后依次截取子串循环判断。循环判断的条件是:如果该单词在哈希表中,就将值-1,如果值变成0,就将该单词从哈希表中删除。
- 如果跳出这个子循环,存在两种情况:该子串符合条件或者不符合条件。则通过判断字典是否为空以及是否循环完整个子串来判断是哪种情况。
- JAVA实现
public class FindSubstring {
public static List<Integer> findSubstring(String s, String[] words) {
List<Integer> pos = new ArrayList();
if(s.isEmpty() || words.length == 0) return pos; //特殊情况:字符串为空或者单词数组为空
int lens = s.length(), lenw = words[0].length();
int total = words.length*lenw;
HashMap hash = new HashMap();
for(String word : words) { //把单词都保存到哈希表中,对应的值是个数
if(!hash.containsKey(word)) hash.put(word,1);
else hash.put(word,(int)hash.get(word)+1);
}
for(int i=0; i<lens-total+1; i++) {
HashMap hashMid = new HashMap(); //哈希也是映射
hashMid.putAll(hash); //因为哈希是映射,浅拷贝(hashMid = hash)一样会改变hash的值,指向同一个地址。因此采用putAll()实现深拷贝,即完全复制内容,而指向新的引用。
String subs = s.substring(i, i+total); //截取子串
int j;
for(j=0; j< total-lenw+1; j+=lenw) {
String key = subs.substring(j, j+lenw); //截取子串的单词
if(hashMid.containsKey(key)) hashMid.put(key, (int)hashMid.get(key)-1); //如果有这个单词,就将它的个数-1
else break;
if((int)hashMid.get(key) == 0) hashMid.remove(key); //如果这个单词的个数为0,就删掉
}
if(hashMid.isEmpty() && j == total) pos.add(i); //如果哈希表为空并且j的值为total,说明是正常循环完毕且符合条件
}
return pos;
}
}