30. Substring with Concatenation of All Words (Hard)

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)

你可能感兴趣的:(30. Substring with Concatenation of All Words (Hard))