Leetcode題解 0011期

0030題 與所有單詞相關聯的字串【Substring with Concatenation of All Words】

題目:
給定一個字符串 s 和一些長度相同的單詞 words。在 s 中找出可以恰好串聯 words 中所有單詞的子串的起始位置。
注意子串要與 words 中的單詞完全匹配,中間不能有其他字符,但不需要考慮 words 中單詞串聯的順序。

示例:

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

題目不嚴謹之處:
1. words應該是不會有空字符串出現的,結果輸入數據仍然有
2. 題目的解釋中多了一個‘r’,在本博客中已經修改並刪掉
3. 題目中說到words中的單詞長度應該是統一的,示例2的words無法滿足題目條件,在本博客中已經修改並刪掉。

需要注意:
1. 不知道示例2是怎麼回事,如果出現不統一長的wordsList,那麼應該直接反饋空List
2. 沒有說明words中不允許出現相同的單詞,所以仍然需要注意
3. Robust

解題思路:
一開始沒有想到特別好的思路,嘗試暴力搜索,代碼如下:

class Solution:
    def __init__(self):
        self.wordsList = {}

    def search(self, s, start, end, l):
        tmp = self.wordsList.copy()
        for i in range(start, end, l):
            local = s[i:i+l]
            if tmp.get(local):
                if tmp[local] > 1:
                    tmp[local] -= 1
                else:
                    del(tmp[local])
                    if len(tmp) == 0:
                        return True
            else:
                return False

    def findSubstring(self, s, words):
        if len(s) == 0 or len(words) == 0: return []
        l = len(words[0])
        cnt = len(words)
        res = []

        for word in words:
            if self.wordsList.get(word):
                self.wordsList[word] += 1
            else:
                self.wordsList[word] = 1

        for i in range(0, len(s)):
            if self.search(s, i, i+cnt*l, l):
                res.append(i)

        return res

emm,然後這段代碼打進去之後,竟然才爆了兩個點,點進去一看那個數據……立即加一下針對樣例的“優化”

if len(self.wordsList) == 1 and self.wordsList.get('a'):
            for i in range(0, len(s)-cnt+1):
                res.append(i)
            return res

其實看了一波提交的答案,發現也都大同小異……之前也說過追求絕對速度是不可取的,所以這裏也直接不管了,理論上來講這道題一定有一些比較好的數據結構可以處理,但是沒有編,然後看了一下解答也並沒有,所以如果有那位大佬哪天突然有興致想編的話,可以在下面評論。


0032題 最長有效括號【Longest Valid Parentheses】

題目:
給定一個只包含 ‘(’ 和 ‘)’ 的字符串,找出最長的包含有效括號的子串的長度。

示例:

輸入: "(()"
輸出: 2
解釋: 最長有效括號子串爲 "()"

輸入: ")()())"
輸出: 4
解釋: 最長有效括號子串爲 "()()"

題目相對嚴謹

除了Robust以外,無需注意太多

解題思路:
1.題目越短的題目,難度往往都略高一些,因爲沒有辦法從題目中獲取更多的信息,自初中正式面對考試時,這種規律就沒有變過。這道題也是,看似非常容易理解的題目,但是想起來確實費一番功夫。
這題暴力搜索複雜度應該是O(n3) ,粗略估計了一下,不過肯定不能使用了,然後覺得括號匹配的題還是用棧來實現,就簡單編了一個短代碼,如下:

class Solution:
    def longestValidParentheses(self, s):
        res = 0
        tmp_valid = 0
        p = list()
        for i in range(len(s)):
            if s[i] == '(':
                p.append(s[i])
            else:
                if len(p) == 0:
                    res = max(res, tmp_valid)
                    tmp_valid = 0
                elif p[-1] == '(':
                    tmp_valid += 2
                    p.pop()

        res = max(res, tmp_valid)
        return res

不過很明顯這個是錯誤的代碼,錯誤樣例如“()(()”,想到這種錯誤樣例,立馬意識到是因爲這段代碼過於早的pop並計算了當下可能最長長度,所以應該在既保留當下可能最長長度,又應該一直維持並沒有匹配的左括號,這裏就要注意新的兩個樣例:“()((()”,“()((())”

class Solution:
    def longestValidParentheses(self, s):
        res = 0
        p = list()
        for i in range(len(s)):
            if s[i] == '(':
                p.append(s[i])
            else:
                if len(p) == 0: continue
                if p[-1] == '(':
                    if len(p) == 1:
                        p.pop()
                        p.append(str(2))
                    else:
                        tmp = 2
                        p.pop()
                        while len(p) > 0 and p[-1].isdigit(): tmp += int(p.pop())
                        p.append(str(tmp))
                elif p[-1].isdigit():
                    if len(p) == 1:
                        res = max(res, int(p[-1]))
                        p = list()
                    else:
                        tmp = int(p.pop()) + 2
                        p.pop()
                        while len(p) > 0 and p[-1].isdigit(): tmp += int(p.pop())
                        p.append(str(tmp))

        for c in p:
            if c.isdigit():
                res = max(res, int(c))

        return res

2.這樣做無非是在不動腦子的模擬操作,這道題的提示中有着動態規劃四個字,思來想去,覺得如果是DP的話,更新點肯定是在右括號,那就是字符串中有什麼規律可循,參考網上的解答貼:https://blog.csdn.net/accepthjp/article/details/52439449,才發現如下規律:

dp[i]表示以當前位置爲終點的最長長度,則只能在)處更新,
如果s[i-1-dp[i-1]]==’(‘,則說明當前位置可以和i-1-dp[i-1]位置匹配,dp[i]=dp[i-1]+2
然後還要加上匹配位置之前的最長長度dp[i]+=dp[i-dp[i]]

自己在草稿紙上演算了一下,還真是這個道理,有點自嘆不如……附上最後的代碼:

class Solution:
    def longestValidParentheses(self, s):
        max_len = 0
        dp = [-1]
        for i in range(len(s)):
            if s[i] == '(':
                dp.append(i)
            else:
                dp.pop()
                if(len(dp) == 0):
                    dp.append(i)
                else:
                    max_len = max(max_len, i - dp[-1])

        return max_len

0036題 有效的數獨【Valid Sudoku】

題目:
判斷一個 9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。

  1. 數字 1-9 在每一行只能出現一次。
  2. 數字 1-9 在每一列只能出現一次。
  3. 數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。
    這是一個部分填充的有效的數獨。

數獨部分空格內已填入了數字,空白格用 ‘.’ 表示。

說明:

  • 一個有效的數獨(部分已被填充)不一定是可解的。
  • 只需要根據以上規則,驗證已經填入的數字是否有效即可。
  • 給定數獨序列只包含數字 1-9 和字符 ‘.’ 。
  • 給定數獨永遠是 9x9 形式的。

示例:

輸入:
[
  ["5","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
輸出: true

輸入:
[
  ["8","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
輸出: false
解釋: 除了第一行的第一個數字從 5 改爲 8 以外,空格內其他數字均與 示例1 相同。
     但由於位於左上角的 3x3 宮內有兩個 8 存在, 因此這個數獨是無效的。

題目相對嚴謹

無需注意太多

解題思路:
【密恐症人慎入的幾道題(棒讀)】
這題僅僅是判斷數獨是否有效,而並不是判斷數獨可否有解,所以工作量頓時降低。
簡單模擬題目就好,這裏不放上那麼冗餘的代碼了。


0037題 解數獨【Sudoku Solver】

題目:
編寫一個程序,通過已填充的空格來解決數獨問題。

一個數獨的解法需遵循如下規則:

  1. 數字 1-9 在每一行只能出現一次。
  2. 數字 1-9 在每一列只能出現一次。
  3. 數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。

說明:

  • 一個有效的數獨(部分已被填充)不一定是可解的。
  • 只需要根據以上規則,驗證已經填入的數字是否有效即可。
  • 給定數獨序列只包含數字 1-9 和字符 ‘.’ 。
  • 給定數獨永遠是 9x9 形式的。

空白格用 ‘.’ 表示。
這是一個數獨
答案是紅色的部分

題目相對嚴謹

經典題目無需注意太多

解題思路:
這是所有搜索題目的經典題,DFS或者BFS都可以解,然後就想到當年看到解數獨時候的Dancing Links……舞蹈鏈算法並不是在這裏用的,會爆炸。

因爲是經典回溯搜索題目,所以就不post代碼了。


0038題 報數【Count and Say】

題目:
報數序列是指一個整數序列,按照其中的整數的順序進行報數,得到下一個數。其前五項如下:

1.     1
2.     11
3.     21
4.     1211
5.     111221

1 被讀作 “one 1” (“一個一”) , 即 11。
11 被讀作 “two 1s” (“兩個一”), 即 21。
21 被讀作 “one 2”, “one 1” (”一個二” , “一個一”) , 即 1211。

給定一個正整數 n ,輸出報數序列的第 n 項。

注意:整數順序將表示爲一個字符串。

示例:

輸入: 1
輸出: "1"

輸入: 4
輸出: "1211"

題目相對嚴謹

除Robust以外,無需注意太多。

解題思路:
emmm,一開始看題感覺有點懵,不知道在說什麼,後來想想無非還是模擬題。
post代碼:

class Solution:
    def nxt(self, s):
        ss = ""
        n = len(s)
        i = 0
        while i < n:
            tmp = i + 1
            while tmp < n and s[tmp] == s[i]: tmp += 1
            ss += str(tmp - i) + s[i]
            i = tmp

        return ss

    def countAndSay(self, n):
        res = ['1']
        for i in range(1, n):
            res.append(self.nxt(res[-1]))

        return res[-1]

如果有數學解法的話,歡迎評論……


0039題 組合總和【Combination Sum】

題目:
給定一個無重複元素的數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和爲 target 的組合。
candidates 中的數字可以無限制重複被選取。
說明:

  • 所有數字(包括 target)都是正整數。
  • 解集不能包含重複的組合。

示例:

輸入: candidates = [2,3,6,7], target = 7,
所求解集爲:
[
  [7],
  [2,2,3]
]

輸入: candidates = [2,3,5], target = 8,
所求解集爲:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
] 

題目相對嚴謹

除Robust以外無需注意太多

解題思路:
好吧,這是一道再經典不過的回溯題目了,直接AC掉,無需post代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章