KK's blog

每天积累多一些

0%

LeetCode



There is a new alien language that uses the English alphabet. However, the order among the letters is unknown to you.

You are given a list of strings words from the alien language’s dictionary, where the strings in words are sorted lexicographically by the rules of this new language.

Return a string of the unique letters in the new alien language sorted in lexicographically increasing order by the new language’s rules. If there is no solution, return "". If there are multiple solutions, return any of them.

A string s is lexicographically smaller than a string t if at the first letter where they differ, the letter in s comes before the letter in t in the alien language. If the first min(s.length, t.length) letters are the same, then s is smaller if and only if s.length < t.length.

Example 1:

Input: words = [“wrt”,”wrf”,”er”,”ett”,”rftt”]
Output: “wertf”


Example 2:

Input: words = [“z”,”x”]
Output: “zx”


Example 3:

Input: words = [“z”,”x”,”z”]
Output: “”
Explanation: The order is invalid, so return "".


Constraints:

1 <= words.length <= 100 1 <= words[i].length <= 100
* words[i] consists of only lowercase English letters.

算法思路:

N/A

注意事项:

  1. 题目要求: 空字符顺序前于非空字母,否则字典不合法,如abc -> ab, c不能前于空字符,无解。简而言之,后面单词不能是前面的前缀return False if len(word) > len(word2) else True
  2. 模板问题: graph要含所有节点,包括没有边的节点。否则结果会有遗漏graph = Counter({c: [] for word in words for c in word})
  3. 模板问题: in_degree初始化要对所有节点赋0, in_degree[c] = 0。in_degree = collections.defaultdict(int)并不能产生key
  4. 模板问题: 第四步判断是否含循环必不可少,题目要求可能不合法,return res if len(graph) == len(res) else ‘’
  5. 语法错误: graph.items()记得加items。res是str不是list

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def alienOrder(self, words: List[str]) -> str:
# graph = collections.defaultdict(list)
graph = Counter({c: [] for word in words for c in word})
for i in range(1, len(words)):
if not self.populate_one_order(words[i - 1], words[i], graph):
return ''
in_degree = collections.defaultdict(int)
for c in graph.keys():
in_degree[c] = 0
for key, li in graph.items():
for j in range(len(li)):
in_degree[li[j]] += 1
res = ''
queue = deque([node for node, in_degree_num in in_degree.items() if in_degree_num == 0])
while queue:
node = queue.popleft()
res += node
for neighbor in graph[node]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
return res if len(graph) == len(res) else ''

def populate_one_order(self, word, word2, graph):
for j in range(min(len(word), len(word2))):
if word[j] != word2[j]:
graph[word[j]].append(word2[j])
return True
return False if len(word) > len(word2) else True

算法分析:

时间复杂度为O(V + E),空间复杂度O(O + E),V为节点数,E为边数,n为单词数,L为最长单词长度,O = nL, E = L,而空间复杂度图的空间为O + E,而queue空间最多为26条边,26个字母,in_degree空间为O(V)。所以总的时间复杂度为O(nL),空间复杂度O(nL)

LeetCode



You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.

Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.

You are given an API bool isBadVersion(version) which returns whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.

Example 1:

Input: n = 5, bad = 4
Output: 4
Explanation:
call isBadVersion(3) -> false
call isBadVersion(5) -> true
call isBadVersion(4) -> true
Then 4 is the first bad version.


Example 2:

Input: n = 1, bad = 1
Output: 1


Constraints:

* 1 <= bad <= n <= 2<sup>31</sup> - 1

算法思路:

N/A

注意事项:

  1. 题目是先good再bad,所以用first position模板

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
def firstBadVersion(self, n):
start, end = 0, n
while start + 1 < end:
mid = start + (end - start) // 2
if isBadVersion(mid):
end = mid
else:
start = mid
if isBadVersion(start):
return start
if isBadVersion(end):
return end
return -1

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int firstBadVersion(int n) {
int start = 1, end = n;
while(start + 1 < end) {
int mid = start + (end - start) / 2;
if(isBadVersion(mid))
end = mid;
else
start = mid;
}
if(isBadVersion(start))
return start;
else
return end;
}

算法分析:

时间复杂度为O(logn),空间复杂度O(1)

LeetCode



Given a pattern and a string s, return true if s matches the pattern.

A string s matches a pattern if there is some bijective mapping of single characters to strings such that if each character in pattern is replaced by the string it maps to, then the resulting string is s. A bijective mapping means that no two characters map to the same string, and no character maps to two different strings.

Example 1:

Input: pattern = “abab”, s = “redblueredblue”
Output: true
Explanation: One possible mapping is as follows:
‘a’ -> “red”
‘b’ -> “blue”


Example 2:

Input: pattern = “aaaa”, s = “asdasdasdasd”
Output: true
Explanation: One possible mapping is as follows:
‘a’ -> “asd”


Example 3:

Input: pattern = “abab”, s = “asdasdasdasd”
Output: true
Explanation: One possible mapping is as follows:
‘a’ -> “a”
‘b’ -> “sdasd”
Note that ‘a’ and ‘b’ cannot both map to “asd” since the mapping is a bijection.


Example 4:

Input: pattern = “aabb”, s = “xyzabcxzyabc”
Output: false


Constraints:

1 <= pattern.length, s.length <= 20 pattern and s consist of only lower-case English letters.

算法思路:

类似于word break,但由于要存储处理过map和set,DP不能处理,所以只能用DFS

注意事项:

  1. 比较映射,用Map比较A->B的映射,如已有a->dog, 另一对映射a->cat通过查找Map知道不合法。B->A的映射可通过将map的所有value存到一个set中知道。如a->dog, b->dog. b不在Map中但b对应的dog在set中,不合法。
    DFS的API为dfs(pattern, word, pattern_to_word, used_set)
  2. 若pattern的字母出现过,如aba,不应进入循环,更不应该加入到map和set中,应该用startswith比较word判断是否合法,若是,直接下一轮DFS(Line 11 -15)
  3. 1中的两情况的第一种情况以及第二种情况的前半部分(b不在map中)在2中已经处理,所以只要在循环中处理第二种情况后半部分(b对应的dog在set中)即可(Line 22 - 23)

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def wordPatternMatch(self, pattern: str, s: str) -> bool:
if not pattern or not s:
return False
return self.dfs(pattern, s, 0, 0, {}, set())

def dfs(self, pattern, s, start_p, start_s, pattern_to_s, s_set):
if start_p >= len(pattern) and start_s >= len(s):
return True
if start_p >= len(pattern) or start_s >= len(s):
return False
char = pattern[start_p]
if char in pattern_to_s:
word = pattern_to_s[char]
if not s[start_s:].startswith(word):
return False
return self.dfs(pattern, s, start_p + 1, start_s + len(word), pattern_to_s, s_set)

for j in range(start_s, len(s)):
matched_word = s[start_s:j + 1]
'''if char in pattern_to_s and pattern_to_s[char] != matched_word:
continue
if char not in pattern_to_s and matched_word in s_set: # remembers
continue'''
if matched_word in s_set:
continue
pattern_to_s[char] = matched_word
s_set.add(matched_word)
if self.dfs(pattern, s, start_p + 1, j + 1, pattern_to_s, s_set):
return True
s_set.remove(matched_word)
pattern_to_s.pop(char)
return False

算法分析:

时间复杂度为O(解大小),空间复杂度为O(解大小)

LeetCode



Implement the RandomizedSet class:

RandomizedSet() Initializes the RandomizedSet object. bool insert(int val) Inserts an item val into the set if not present. Returns true if the item was not present, false otherwise.
bool remove(int val) Removes an item val from the set if present. Returns true if the item was present, false otherwise. int getRandom() Returns a random element from the current set of elements (it’s guaranteed that at least one element exists when this method is called). Each element must have the same probability of being returned.

You must implement the functions of the class such that each function works in average O(1) time complexity.

Example 1:

Input
[“RandomizedSet”, “insert”, “remove”, “insert”, “getRandom”, “remove”, “insert”, “getRandom”]
[[], [1], [2], [2], [], [1], [2], []]
Output
[null, true, false, true, 2, true, false, 2]

Explanation
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1); // Inserts 1 to the set. Returns true as 1 was inserted successfully.
randomizedSet.remove(2); // Returns false as 2 does not exist in the set.
randomizedSet.insert(2); // Inserts 2 to the set, returns true. Set now contains [1,2].
randomizedSet.getRandom(); // getRandom() should return either 1 or 2 randomly.
randomizedSet.remove(1); // Removes 1 from the set, returns true. Set now contains [2].
randomizedSet.insert(2); // 2 was already in the set, so return false.
randomizedSet.getRandom(); // Since 2 is the only number in the set, getRandom() will always return 2.


Constraints:

-2<sup>31</sup> <= val <= 2<sup>31</sup> - 1 At most 2 * ``10<sup>5</sup> calls will be made to insert, remove, and getRandom.
There will be *at least one element in the data structure when getRandom is called.

算法思路:

Dict + List

注意事项:

  1. 检查若删除最后一个元素发现问题,remove中删除key要放在最后,不能放中间

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class RandomizedSet(TestCases):

def __init__(self):
self.nums = []
self.key_to_index = {}

def insert(self, val: int) -> bool:
if val in self.key_to_index:
return False
self.nums.append(val)
self.key_to_index[val] = len(self.nums) - 1
return True

def remove(self, val: int) -> bool:
if val not in self.key_to_index:
return False
index = self.key_to_index[val]
last_val = self.nums[len(self.nums) - 1]
self.nums[index] = last_val
self.key_to_index[last_val] = index
self.key_to_index.pop(val) # remember to put it last
self.nums.pop()
return True

def getRandom(self) -> int:
return self.nums[random.randint(0, len(self.nums) - 1)]

算法分析:

时间复杂度为O(1),空间复杂度O(n).

LeetCode



Given a string s containing only three types of characters: '(', ')' and '*', return true if s is valid.

The following rules define a valid string:

Any left parenthesis '(' must have a corresponding right parenthesis ')'. Any right parenthesis ')' must have a corresponding left parenthesis '('.
Left parenthesis '(' must go before the corresponding right parenthesis ')'. '*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string "".

Example 1:

Input: s = “()”
Output: true


Example 2:

Input: s = “()”
Output: true


Example 3:

Input: s = “())”
Output: true


Constraints:

1 <= s.length <= 100 s[i] is '(', ')' or '*'.

题目大意:

求给定字符串带星号是否合法括号配对。

Stack算法思路(推荐):

括号题优先考虑用Stack。如果不带星号,回忆合法括号题,有三种不合法情况,此题只需考虑两种,不需考虑多种括号类型
三种不合法情况: ‘[‘ (stack有余), ‘]’ (要匹配的时候stack为空)
难点:

  1. 在于要去想多一个栈来存星号,因为星号可以作为左括号备选去match右括号。右括号在两个栈中优先配对左括号,星号可以为空。如果两个栈均为空,处理了第一种不合法情况
  2. 循环后,如果两栈有余,分4中情况讨论:
    1) 左括号栈有余星号栈空,正是第二种不合法情况
    2) 左括号栈空星号栈空,合法
    3) 左括号栈空星号栈有余,合法,星号可为空
    4) 都有余,这是难点二。星号可以作为右括号去配对左括号,前提条件是星号在左括号之后,考虑*(,这是不合法

注意事项:

  1. 如果for循环出来后,两栈不为空,要比较先后顺序
  2. for loop后,L18 - Line 19记得pop,否则死循环

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def checkValidString(self, s: str) -> bool:
stack_left, stack_star = [], []
for i in range(len(s)):
if s[i] == '(':
stack_left.append(i)
if s[i] == '*':
stack_star.append(i)
if s[i] == ')':
if stack_left: # match ( first rather than * because * can be empty
stack_left.pop()
elif stack_star:
stack_star.pop()
else:
return False
while stack_left and stack_star: # use * to match (
if stack_left[-1] > stack_star[-1]: # consider *(
return False
stack_left.pop()
stack_star.pop()
return len(stack_left) == 0 # stack_star can be non empty

算法分析:

时间复杂度为O(n),空间复杂度O(n)


统计算法II解题思路:

括号题另一个常用思路是用统计左右括号数。此题较难想到是用一个左括号数量范围去验证。
lo为左括号的最少合法个数,hi为左括号的最大合法个数,有范围是因为星号可以变成左右括号或空。
遇到左括号,都加1,遇到右括号,都减1,遇到星号,假设星号为右括号,所以lo减1,hi加1.
如果hi小于0,表示最大左括号数小于右括号数,不满足此法的规则一,不合法

难点在于lo设为非负。因为lo是最少且合法,合法意思是lo不是单纯地将所有星号变成右括号,而是当左括号不足时,用提高下限,将星号变成空,体现在令lo为非负。
for循环后,lo必须为0,运用了法则二,左右括号相等。

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def checkValidString(self, s: str) -> bool:
lo = hi = 0
for char in s:
if char == '(':
lo += 1
hi += 1
if char == '*':
if lo > 0: # treat * as empty space
lo -= 1
hi += 1
if char == ')':
if lo > 0: # treat the previous * as empty space
lo -= 1
hi -= 1
if hi < 0: # the num of right parenthesis > left ones
return False
return lo == 0 # the num of right parenthesis should equal to left ones

算法分析:

时间复杂度为O(n),空间复杂度O(n)


DP算法III解题思路:

基本情况为s[i], s[j] 分别在(*, )* 就合法
如果用单边DP,并不能确定区间内那些合法,所以只能用区间型DP
dp[i][j] = s[i-1] == ‘*‘ and dp[i+1][j] 星号不匹配
= s[i-1] in ‘(*‘ and dp[i+1][k-1] and s[k-1] in (‘)*‘) and dp[k+1][j] 星号匹配

具体参考leetcode答案
DP基本情况比较难想出来且递归是复杂,实现易错,不推荐。不过可以多了解区间型DP的模式

算法分析:

时间复杂度为O(n^3),空间复杂度O(n^2)

Free mock interview