1. 程式人生 > >Leetcode 30:與所有單詞相關聯的字串(超詳細的解法!!!)

Leetcode 30:與所有單詞相關聯的字串(超詳細的解法!!!)

給定一個字串 s 和一些長度相同的單詞 **words。**在 s 中找出可以恰好串聯 words 中所有單詞的子串的起始位置。

注意子串要與 words 中的單詞完全匹配,中間不能有其他字元,但不需要考慮 words 中單詞串聯的順序。

示例 1:

輸入:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
輸出: [0,9]
解釋: 從索引 0 和 9 開始的子串分別是 "barfoor" 和 "foobar" 。
輸出的順序不重要, [9,0] 也是有效答案。

示例 2:

輸入:
  s = "wordgoodstudentgoodword",
  words = ["word","student"]
輸出: []

解題思路

我們首先想到的解法當然是暴力破解。我們將words的所有排列情況列出來,為了避免重複元素,我們應該使用一個set去存放結果。然後我們遍歷set中的所有元素,檢視元素在s中的位置,並將位置存放到list中。

class Solution:
    def findSubstring(self, s, words):
        """
        :type s: str
        :type words: List[str]
        :rtype: List[int]
        """
        res = list()
        if
not words: return res for word in set(itertools.permutations(words)): tmp_word = ''.join(word) i = s.find(tmp_word) while i != -1: res.append(i) i = s.find(tmp_word, i + 1) return
res

提交程式碼後提示我們Memory Limit Exceeded。我們遺漏了一個條件長度相同的單詞 words。所以我們就有了如下的策略,我們可以先將words中的元素存放到dict中,對於例1來說就是

此時我們的目標就是匹配dict中的所有字串,我們從頭開始

我們發現此時匹配了bar,所以我們此時應該移動len(words[0])的步數

我們發現此時foo也匹配了,所以此時dict中的全部元素都匹配成功,我們就要記錄開始的index=0。我們現在從index=1開始匹配

我們發現第一個字串就匹配失敗,我們接著從index=2開始也是失敗,我們接從index=3開始

我們發現此時第一個匹配成功,所以我們此時應該移動len(words[0])

我們發現此時匹配失敗,所以我們就要從index=4,依次往後。

class Solution:
    def findSubstring(self, s, words):
        """
        :type s: str
        :type words: List[str]
        :rtype: List[int]
        """
        if not words:
            return []
        
        words_dict = collections.defaultdict(int)
        for word in words:
            words_dict[word] += 1
            
        s_len, words_len, word_len, res = len(s), len(words), len(words[0]), list()
        for i in range(s_len - words_len*word_len + 1):
            num, has_words = 0, collections.defaultdict(int)
            while num < words_len:
                word = s[i+num*word_len:i+(num+1)*word_len]
                if word not in words_dict:
                    break

                has_words[word] += 1
                if has_words[word] > words_dict[word]:
                    break
                
                num += 1
                
            if num == words_len:
                res.append(i)
                
        return res          

上述程式碼提交後就獲得了accept。你可能已經注意到了上述演算法中存在的一些缺陷,例如當我們的words很長時,我們n-1word都匹配成功了,但是就最後一個匹配失敗,我們就要index++開始,實際上這是有問題的,我們此時應該繼續從失敗的單詞後繼續開始匹配,直到這一輪匹配完再從index++開始,例如

我們發現此時匹配失敗,我們應該從the後,也就是foo開始將剩餘部分匹配完。

這樣我們就充分利用了我們之前儲存的資訊。為了更好地說明問題,我們取這樣的一個例子s = "barfoobarfoobarfoofoo",words = ["foo","bar","foo"]。我們來看一下具體實現步驟

我們發現此時匹配到了一個單詞bar,所以此時我們前進len(words[0])步,並且將此時匹配到的單詞加入到一個臨時的字典中存放。

我們發現此時匹配到了一個單詞foo,所以此時我們前進len(words[0])步,並且將此時匹配到的單詞加入到一個臨時的字典中存放。

我們發現此時匹配到了一個單詞bar,但是此時bar的數量已經超過了words中的,所以我們此時要將第一個bar彈出。

我們發現此時匹配到的單詞the不在words中,所以此時我們前進len(words[0])步,並且將臨時字典清空。

我們發現此時匹配到了一個單詞bar,所以此時我們前進len(words[0])步,並且將此時匹配到的單詞加入到一個臨時的字典中存放。

我們發現此時匹配到了一個單詞foo,所以此時我們前進len(words[0])步,並且將此時匹配到的單詞加入到一個臨時的字典中存放。

我們發現此時匹配到了一個單詞foo,並且將此時匹配到的單詞加入到一個臨時的字典中存放,我們發現此時多有單詞匹配成功,我們就將index=12加入到我們的結果中。接著我們再從index=1...len(words[0])-1開始遍歷即可。程式碼如下

class Solution:
    def findSubstring(self, s, words):
        """
        :type s: str
        :type words: List[str]
        :rtype: List[int]
        """
        if not words:
            return []
        
        words_dict = collections.defaultdict(int)
        for word in words:
            words_dict[word] += 1
            
        s_len, words_len, word_len, res = len(s), len(words), len(words[0]), list()
        for k in range(word_len):
            has_words, num = collections.defaultdict(int), 0
            for i in range(k, s_len, word_len):
                word = s[i:i + word_len]
                if word in words_dict:
                    num += 1
                    has_words[word] += 1
                    while has_words[word] > words_dict[word]:
                        pos = i - word_len*(num - 1)
                        rem_word = s[pos:pos + word_len]
                        has_words[rem_word] -= 1
                        num -= 1
                else:
                    has_words.clear()
                    num = 0
                    
                if num == words_len:
                    res.append(i - word_len*(num - 1))
        return res

我將該問題的其他語言版本新增到了我的GitHub Leetcode

如有問題,希望大家指出!!!