From e02c9f8040c1810ffeaca3dbe3c09b872bf7b38e Mon Sep 17 00:00:00 2001 From: Symbolk Date: Tue, 17 Dec 2019 20:52:24 +0800 Subject: [PATCH] Homework for Week08. --- Week 08/id_556/LeetCode_127_556.py | 67 +++++++++++++++++ Week 08/id_556/LeetCode_300_556.py | 64 +++++++++++++++++ Week 08/id_556/LeetCode_91_556.py | 48 +++++++++++++ Week 08/id_556/NOTE.md | 111 +++++++++++++++++++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 Week 08/id_556/LeetCode_127_556.py create mode 100644 Week 08/id_556/LeetCode_300_556.py create mode 100644 Week 08/id_556/LeetCode_91_556.py diff --git a/Week 08/id_556/LeetCode_127_556.py b/Week 08/id_556/LeetCode_127_556.py new file mode 100644 index 000000000..a714a2f66 --- /dev/null +++ b/Week 08/id_556/LeetCode_127_556.py @@ -0,0 +1,67 @@ +from typing import List + + +class Solution(object): + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + wordSet = set(wordList) + if endWord not in wordSet: + return 0 + + res = 1 + n = len(beginWord) + current = set() + current.add(beginWord) + + while current: + next_level = set() + if endWord in current: + return res + for cur in current: + if cur in wordSet: + wordSet.remove(cur) + for i in range(n): + for ch in range(97, 123): + tmp = cur[:i] + chr(ch) + cur[i + 1:] + if tmp in wordSet: + next_level.add(tmp) + res += 1 + current = next_level + return 0 + + def ladderLength2(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + wordSet = set(wordList) + if endWord not in wordSet: + return 0 + + res = 1 + wordDict = {} + for w in wordSet: + for i in range(len(w)): + new_w = w[:i] + "_" + w[i + 1:] + if new_w in wordDict: + wordDict[new_w].append(w) + else: + wordDict[new_w] = [w] + + current = [beginWord] + next_level = [] + while current: + for w in current: + if w == endWord: + return res + for i in range(len(w)): + new_w = w[:i] + "_" + w[i + 1:] + if new_w in wordDict: + for t in wordDict[new_w]: + if t not in next_level: + next_level.append(t) + del wordDict[new_w] + res += 1 + current = next_level + next_level = [] + return 0 + + +if __name__ == "__main__": + solution = Solution() + print(solution.ladderLength2("hit", "cog", ["hot", "dot", "dog", "lot", "log", "cog"])) diff --git a/Week 08/id_556/LeetCode_300_556.py b/Week 08/id_556/LeetCode_300_556.py new file mode 100644 index 000000000..60025b0e0 --- /dev/null +++ b/Week 08/id_556/LeetCode_300_556.py @@ -0,0 +1,64 @@ +from typing import List + + +# O(n^2) +def lengthOfLIS(nums: List[int]) -> int: + if not nums: + return 0 + n = len(nums) + dp = [1] * n + for i in range(n): + for j in range(i): + if nums[j] < nums[i]: + dp[i] = max(dp[i], dp[j] + 1) + else: + continue + return max(dp) + + +# O(nlogn) +def lengthOfLIS2(nums): + if not nums: + return 0 + n = len(nums) + if n < 2: + return n + # the min tail element in nums[:i+1] + tails = [nums[0]] + for i in range(1, n): + if nums[i] > tails[-1]: + tails.append(nums[i]) + continue + # binary search to find the first tail > nums[i] + l, r = 0, len(tails) - 1 + while l <= r: + m = l + ((r - l) >> 1) + if tails[m] < nums[i]: + l = m + 1 + else: + r = m - 1 + tails[l] = nums[i] + return len(tails) + + +def lengthOfLIS3(nums): + n = len(nums) + tails, res = [0] * n, 0 + for num in nums: + i, j = 0, res + while i < j: + m = i + ((j - i) >> 1) + if tails[m] < num: + i = m + 1 + else: + j = m + tails[i] = num + if j == res: + res += 1 + return res + + +nums = [10, 9, 2, 5, 3, 7, 101, 18] +print(lengthOfLIS(nums)) +print(lengthOfLIS2(nums)) +print(lengthOfLIS3(nums)) diff --git a/Week 08/id_556/LeetCode_91_556.py b/Week 08/id_556/LeetCode_91_556.py new file mode 100644 index 000000000..48c26a3ae --- /dev/null +++ b/Week 08/id_556/LeetCode_91_556.py @@ -0,0 +1,48 @@ +# O(n) time/space +def numDecodings(s: str) -> int: + n = len(s) + if not s or s[0] == "0": + return 0 + # dp[i] denotes the num of decoding for s[:i] + dp = [0] * (n + 1) + dp[0] = 1 + dp[1] = 1 + for i in range(1, n): + if s[i] == "0": + if s[i - 1] == "1" or s[i - 1] == "2": + dp[i + 1] = dp[i - 1] + else: + return 0 + else: + if s[i - 1] == "1" or (s[i - 1] == "2" and "1" <= s[i] <= "6"): + dp[i + 1] = dp[i] + dp[i - 1] + else: + dp[i + 1] = dp[i] + return dp[-1] + + +# O(n) time, O(1) space +def numDecodings2(s): + n = len(s) + if not s or s[0] == "0": + return 0 + pre, cur = 1, 1 + for i in range(1, n): + if s[i] == "0": + if s[i - 1] == "1" or s[i - 1] == "2": + cur = pre + else: + return 0 + else: + if s[i - 1] == "1" or (s[i - 1] == "2" and "1" <= s[i] <= "6"): + tmp = cur + cur += pre + pre = tmp + else: + pre = cur + return cur + + +print(numDecodings("0")) +print(numDecodings2("226")) +print("226"[:2]) diff --git a/Week 08/id_556/NOTE.md b/Week 08/id_556/NOTE.md index a6321d6e2..f43329d3e 100755 --- a/Week 08/id_556/NOTE.md +++ b/Week 08/id_556/NOTE.md @@ -2,3 +2,114 @@ +## Week8-19-动态规划 + +### 基础DP + +大部分动态规划最简化的程序往往是动态递推,利用数学归纳法从之前的DP状态循环递推到最终的解。 + +1. DP状态的定义 +2. 状态转移方程 +3. 最终解位置 + +动态规划和递归或分治没有本质区别,共性为找重复子问题,关键看有无最优子结构,动规中途可以淘汰次优解 + +dp[i][j] = min(dp[i-1][j], dp[i-1][j] + A[i][j]) + + +### 高级DP + +1. 状态拥有更多的维度 +2. 状态方程更为复杂 +3. 本质考察逻辑思维和数学能力等内功 + +关键需要确定: + +1. 维度(定义、压缩) +2. 状态转移方程 +3. 初始化和边界条件 + +> 面试中状态定义比较重要,竞赛中递归方程会比较难 + +> 多练多思考,提升数学能力和抽象思维 + +#### 实例 + +##### 9. 股票买卖(121) + +三维DP搞定股票买卖系列: + + +##### 10. Edit Distance(72) + + +BFS或双向BFS + +字符串变换:(一般使用)二维数组定义DP状态 + +> 隐含条件:两个字符串的长度在变换中相互靠近;增加和删除是对等的操作 + +dp[i][j]: word1[0:i]与word[0:j]之间的编辑距离 + +w1[-1] == w[-1] ? + +Y: dp[i][j] = dp[i-1, j-1] + +N: min(dp[i-1, j-1] + 1, dp[i-1][j]+1, dp[i, j-1] + 1) + +## 字符串算法 + +字符串可变语言: + +C/C++, Ruby, PHP, Swift + +字符串不可变语言: + +Java, Python, C#, Javascript, Go + +### DP+String + +1. 编辑距离 + +dp[i][j] = dp[i-1][j-1] if w1[i]==w2[j] else min(dp[i-1][j-1], dp[i-1]][j], dp[i][j-1])+1 + +2. 最长公共子序列 + +dp[i][j] = dp[i-1][j-1] + 1 if s1[i] == s2[j] else max(dp[i-1][j], dp[i][j-1]) + +3. 最长公共子串 + +dp[j][j] = dp[i-1][j-1] + 1 if s1[i] == s2[j] else 0 + +4. 最长回文子序列 + +- 暴力枚举起点和终点,判断子串是否回文 +- 枚举子串中心的一个字符(奇数长度)或中心两个(偶数长度),判断两边对称的字符长度 +- DP:dp[i][j]=(dp[i+1][j-1] || (j-i)<2) if s[i]==s[j] + +5. 正则表达式 + + +6. 不同子序列 + +### 字符串匹配算法 (知道原理可以自己解释即可) + +> 给定两个字符串,问其中一个字符串在另一个字符串中出现的起点 + +1. BrutalForce O(mn) + +高级算法都是在暴力枚举的基础上进行优化 + +2. Rabin-Karp + +用hash值先过滤掉不可能相同的子串起点;如果hash值相同,再逐个字符比较 + +但如果每次都是先取子串再调用系统hash进行计算的话,因hash函数会遍历子串,复杂度其实还是O(mn) + +使用滑动窗口进行加速:O(1)来更新hash值,从而整体上平均达到O(n)的复杂度 + +3. KMP + +找已匹配片段最大前缀和最大后缀的最长长度 + +insight:当知道在某个位置不匹配时,其实已经知道了前面能匹配的子串,可以利用已知匹配往后挪动 \ No newline at end of file