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 sthat 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 = "wordgoodstudentgoodword",
words = ["word","student"]
Output: []
/*******************************************************/
一道比看起来要复杂的题目:
- 我是用最naive的方法实现的:遍历words数组,把每个word存到hashmap里去。然后遍历字符串s的时候,对每lw个字符(lw是每个word的字长)匹配一下,看hashmap当中是否存在。如果存在的话就尝试匹配完整的words数组。复杂之处在于words数组里的word可能有重复的,因此需要在匹配时考虑到重复的问题。显然这种方法效率很低,假设words数组有n个word,s字符串长l,lw是每个word的字长,那么时间复杂度是O(n*l),非常慢,但是万幸也能够通过测试。
- 一个进阶版的算法是引入时间窗的概念:例如从字符串0的位置开始,每lw个字符匹配一次——如果第一次匹配成功,那么在此处记录下pos1,作为时间窗的起点;如果不是第一次匹配成功,那么继续向后匹配,直到从pos1到此处的字符串可以完全匹配words时,说明pos1是一个可行的点,然后将pos1向下一个窗口滑动,继续匹配;如果匹配失败,说明pos1不是一个可行的点,依旧将pos1向下一个窗口滑动,然后重新匹配。这样的步骤只需要重复lw次,就可以完成对整个s串的匹配。这种方法的优势在于,时间窗口可以将从pos1开始到当前位置的所有匹配信息记录下来,有效地加快了速度,时间复杂度在O(l*lw),LeetCode里速度前50%的基本都是这种方法了。
- 似乎还有一种更快的方法,只需要O(n*ls+l)的时间复杂度,使用的是AC自动机的方法,我没有详细了解,在这里只做一个记录,将来有需要的时候可以了解一下。
最后记录一下我的代码,用的是最naive的方法,语言是java:
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
ArrayList<Integer> result = new ArrayList<Integer>(0);
HashMap<String,Integer> MapForString = new HashMap<>();
int Statistic[] = new int [words.length];
for(int t = 0 ;t<words.length;t++)
{
if(MapForString.get(words[t])==null)
{
MapForString.put(words[t], t);
Statistic[t]=1;
}
else
Statistic[MapForString.get(words[t])]++;
}
if(s.length()==0 || words.length == 0)
return result;
int WordL= words[0].length();
for(int i = 0;i+WordL-1<s.length();i++)
{
String temp = s.substring(i, i+WordL);
if(MapForString.get(temp)!=null)
{
boolean flag = true;
int record[] = new int[words.length];
for(int k = 0;k<words.length;k++)
{
record[k] =Statistic[k];
}
record[MapForString.get(temp)]--;
int count = 1;
for(int j = 1;i+j*WordL+WordL-1<s.length() && j<words.length;j++)
{
temp = s.substring(i+j*WordL,i+j*WordL+WordL);
if(MapForString.get(temp)!=null
&& record[MapForString.get(temp)]>0)
{
record[MapForString.get(temp)]--;
count++;
}
else
{
flag = false;
break;
}
}
if(flag && count == words.length)
{
result.add(i);
}
}
}
return result;
}
}