Description:
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: []
Solutions:
Solution1:
class Solution:
def findSubstring(self, s: str, words):
if not s or not words:
return []
divisor = len(words[0])
if divisor == 0:
return list(range(len(s)+1))
if len(words[0])*len(words) > len(s):
return []
dic = []
for n in range(divisor):
dic.append([])
cmp = {}
cache = {}
for w in words:
if w not in cmp:
cmp[w] = 1
cache[w] = 0
else:
cmp[w] += 1
for i in range(len(s)-divisor+1):
substring = s[i:i+divisor]
if substring in cmp:
quotient = i//divisor
reminder = i%divisor
dic[reminder].append([quotient,substring])
result = []
for reminder in range(divisor):
pair = dic[reminder]
i = 0
while(i < len(pair)-len(words)+1):
flag = 0
for j in range(len(words)):
pair_ij = pair[i+j]
pair_i = pair[i]
if pair_ij[0] != pair_i[0] + j:
i += j
flag = 1
break
else:
cache[pair_ij[1]] += 1
if cache[pair_ij[1]] > cmp[pair_ij[1]]:
flag = 2
break
if flag == 0:
result.append(pair_i[0]*divisor+reminder)
i += 1
elif flag == 2:
i += 1
for k in cache:
cache[k] = 0
return result
Performance:
- Runtime: 332 ms, faster than 56.84% of Python3 online submissions for Substring with Concatenation of All Words.
- Memory Usage: 14.8 MB, less than 5.08% of Python3 online submissions for Substring with Concatenation of All Words.
Wrong Solution: TLE on https://leetcode.com/submissions/detail/238382050/testcase/
import itertools
class Solution:
def findSubstring(self, s: str, words):
if not s or not words:
return []
if not words[0]:
return list(range(len(s)+1))
if len(words[0])*len(words) > len(s):
return []
result = set()
for p in itertools.permutations(words):
string = ""
for w in p:
string += w
for n in range(len(s)):
if s.find(string,n) == n:
result.add(n)
return list(result)
Other solutions: 把C++代码翻译成了Python,但是不知道为什么Python的版本这么慢
https://www.cnblogs.com/grandyang/p/4521224.html
// time usage: 30ms
class Solution {
public:
vector findSubstring(string s, vector& words) {
if (s.empty() || words.empty()) return {};
vector res;
int n = s.size(), cnt = words.size(), len = words[0].size();
unordered_map m1;
for (string w : words) ++m1[w];
for (int i = 0; i < len; ++i) {
int left = i, count = 0;
unordered_map m2;
for (int j = i; j <= n - len; j += len) {
string t = s.substr(j, len);
if (m1.count(t)) {
++m2[t];
if (m2[t] <= m1[t]) {
++count;
} else {
while (m2[t] > m1[t]) {
string t1 = s.substr(left, len);
--m2[t1];
if (m2[t1] < m1[t1]) --count;
left += len;
}
}
if (count == cnt) {
res.push_back(left);
--m2[s.substr(left, len)];
--count;
left += len;
}
} else {
m2.clear();
count = 0;
left = j + len;
}
}
}
return res;
}
};
# time usage = 328 ms
class Solution:
def findSubstring(self, s: str, words):
if not s or not words:
return []
divisor = len(words[0])
if divisor == 0:
return list(range(len(s)+1))
total_length = len(words[0])*len(words)
if total_length > len(s):
return []
cmp = {}
cache = {}
for w in words:
if w not in cmp:
cmp[w] = 1
cache[w] = 0
else:
cmp[w] += 1
result = []
for reminder in range(divisor):
count = 0
left = reminder
for j in range(reminder,len(s)-divisor+1,divisor):
substring = s[j:j+divisor]
if substring in cmp:
cache[substring] += 1
count += 1
while cache[substring] > cmp[substring]:
cache[s[left:left+divisor]] -= 1
left += divisor
count -= 1
if count == len(words):
result.append(left)
cache[s[left:left+divisor]] -= 1
left += divisor
count -= 1
else:
for c in cache:
cache[c] = 0
count = 0
left = j+divisor
for c in cache:
cache[c] = 0
return result
Solution: sample 36 ms submission
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
if not s or words==[]:
return []
lenstr=len(s)
lenword=len(words[0])
lensubstr=len(words)*lenword
times={}
for word in words:
if word in times:
times[word]+=1
else:
times[word]=1
ans=[]
for i in range(min(lenword,lenstr-lensubstr+1)):
self.findAnswer(i,lenstr,lenword,lensubstr,s,times,ans)
return ans
def findAnswer(self,strstart,lenstr,lenword,lensubstr,s,times,ans):
wordstart=strstart
curr={}
while strstart+lensubstr<=lenstr:
word=s[wordstart:wordstart+lenword]
wordstart+=lenword
if word not in times:
strstart=wordstart
curr.clear()
else:
if word in curr:
curr[word]+=1
else:
curr[word]=1
while curr[word]>times[word]:
curr[s[strstart:strstart+lenword]]-=1
strstart+=lenword
if wordstart-strstart==lensubstr:
ans.append(strstart)