目錄
1-Unique Number of Occurrences-easy。哈希表
2-Get Equal Substrings Within Budget-medium。滑動窗口
3-Remove All Adjacent Duplicates in String II-medium。棧
4-Minimum Moves to Reach Target with Rotations-hard。BFS
1-Unique Number of Occurrences-easy。哈希表
Given an array of integers arr
, write a function that returns true
if and only if the number of occurrences of each value in the array is unique.
Example 1:
Input: arr = [1,2,2,1,1,3]
Output: true
Explanation: The value 1 has 3 occurrences, 2 has 2 and 3 has 1. No two values have the same number of occurrences.
Example 2:
Input: arr = [1,2]
Output: false
Example 3:
Input: arr = [-3,0,1,-3,1,1,1,-3,10,0]
Output: true
Constraints:
1 <= arr.length <= 1000
-1000 <= arr[i] <= 1000
比較簡單,考察字典
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
if not arr:
return True
dic = {}
for n in arr:
dic[n] = dic.get(n, 0) + 1
return len(set(dic.values())) == len(set(dic.keys()))
2-Get Equal Substrings Within Budget-medium。滑動窗口
You are given two strings s
and t
of the same length. You want to change s
to t
. Changing the i
-th character of s
to i
-th character of t
costs |s[i] - t[i]|
that is, the absolute difference between the ASCII values of the characters.
You are also given an integer maxCost
.
Return the maximum length of a substring of s
that can be changed to be the same as the corresponding substring of t
with a cost less than or equal to maxCost
.
If there is no substring from s
that can be changed to its corresponding substring from t
, return 0
.
Example 1:
Input: s = "abcd", t = "bcdf", cost = 3
Output: 3
Explanation: "abc" of s can change to "bcd". That costs 3, so the maximum length is 3.
Example 2:
Input: s = "abcd", t = "cdef", cost = 3
Output: 1
Explanation: Each charactor in s costs 2 to change to charactor in t, so the maximum length is 1.
Example 3:
Input: s = "abcd", t = "acde", cost = 0
Output: 1
Explanation: You can't make any change, so the maximum length is 1.
Constraints:
1 <= s.length, t.length <= 10^5
0 <= maxCost <= 10^6
s
andt
only contain lower case English letters.
比較簡單,滑動窗口的模板題
class Solution:
def equalSubstring(self, s: str, t: str, maxCost: int) -> int:
if not s or not t or len(s) != len(t):
return 0
left = cur_cost = res = 0
n = len(s)
for i in range(n):
cur_cost += abs(ord(s[i]) - ord(t[i]))
while cur_cost > maxCost:
cur_cost -= abs(ord(s[left]) - ord(t[left]))
left += 1
res = max(res, i-left+1)
return res
3-Remove All Adjacent Duplicates in String II-medium。棧
Given a string s
, a k duplicate removal consists of choosing k
adjacent and equal letters from s
and removing them causing the left and the right side of the deleted substring to concatenate together.
We repeatedly make k
duplicate removals on s
until we no longer can.
Return the final string after all such duplicate removals have been made.
It is guaranteed that the answer is unique.
Example 1:
Input: s = "abcd", k = 2
Output: "abcd"
Explanation: There's nothing to delete.
Example 2:
Input: s = "deeedbbcccbdaa", k = 3
Output: "aa"
Explanation:
First delete "eee" and "ccc", get "ddbbbdaa"
Then delete "bbb", get "dddaa"
Finally delete "ddd", get "aa"
Example 3:
Input: s = "pbbcggttciiippooaais", k = 2
Output: "ps"
Constraints:
1 <= s.length <= 10^5
2 <= k <= 10^4
s
only contains lower case English letters.
棧的題。題意比較清楚,難的是如何寫的比較簡潔。之前想的是如果只遍歷一次的話,無法通過頻數K去pop元素,但其實棧內元素可以記錄每個字符出現的頻數,遇到相同的字符看看頻數有否超過k,一旦==k就會被pop。
# DIY的做法
class Solution:
def removeDuplicates(self, s: str, k: int) -> str:
if not s or k == 0:
return s
if k == 1:
return ""
s_lst = list(s)
st = []
while True:
tmp = []
n = len(s_lst)
flag = False
i = 0
while i < n:
if i+1 < n and s_lst[i] == s_lst[i+1]:
j = i
while j+1 < n and s_lst[j] == s_lst[j+1]:
tmp.append(s_lst[j])
j += 1
if j-i+1 >= k:
flag = True
remain = (j-i+1)%k
for m in range(remain):
st.append(s_lst[i])
else:
for m in range(i, j+1):
st.append(s_lst[m])
i = j+1
else:
st.append(s_lst[i])
i += 1
if not flag or not st:
break
s_lst = st
st = []
return ''.join(st)
# 簡潔的做法
class Solution:
def removeDuplicates(self, s: str, k: int) -> str:
if not s or k == 0:
return s
if k == 1:
return ""
st = []
n = len(s)
for i in range(n):
if not st or st[-1][0] != s[i]:
st.append([s[i], 1])
else:
if st[-1][1] + 1 >= k:
st.pop()
else:
st[-1][1] += 1
ans = []
for n in st:
ans.append(n[0]*n[1])
return ''.join(ans)
4-Minimum Moves to Reach Target with Rotations-hard。BFS
In an n*n
grid, there is a snake that spans 2 cells and starts moving from the top left corner at (0, 0)
and (0, 1)
. The grid has empty cells represented by zeros and blocked cells represented by ones. The snake wants to reach the lower right corner at (n-1, n-2)
and (n-1, n-1)
.
In one move the snake can:
- Move one cell to the right if there are no blocked cells there. This move keeps the horizontal/vertical position of the snake as it is.
- Move down one cell if there are no blocked cells there. This move keeps the horizontal/vertical position of the snake as it is.
- Rotate clockwise if it's in a horizontal position and the two cells under it are both empty. In that case the snake moves from
(r, c)
and(r, c+1)
to(r, c)
and(r+1, c)
. - Rotate counterclockwise if it's in a vertical position and the two cells to its right are both empty. In that case the snake moves from
(r, c)
and(r+1, c)
to(r, c)
and(r, c+1)
.
Return the minimum number of moves to reach the target.
If there is no way to reach the target, return -1
.
Example 1:
Input: grid = [[0,0,0,0,0,1],
[1,1,0,0,1,0],
[0,0,0,0,1,1],
[0,0,1,0,1,0],
[0,1,1,0,0,0],
[0,1,1,0,0,0]]
Output: 11
Explanation:
One possible solution is [right, right, rotate clockwise, right, down, down, down, down, rotate counterclockwise, right, down].
Example 2:
Input: grid = [[0,0,1,1,1,1],
[0,0,0,0,1,1],
[1,1,0,0,0,1],
[1,1,1,0,0,1],
[1,1,1,0,0,1],
[1,1,1,0,0,0]]
Output: 9
Constraints:
2 <= n <= 100
0 <= grid[i][j] <= 1
- It is guaranteed that the snake starts at empty cells.
原先以爲是比較典型的BFS,但其實是道難題,需要考慮理解清楚題意,就是蛇身有哪幾個變換方向,在它是橫着還是豎着的時候能進行什麼位置變換是不同的。
- 三維visited數組的設定要增加一個標誌位就是橫豎,如果只打算把蛇身抽象成一個點,比如在此選取蛇尾
- 但以傳統的BFS視角也能做。能唯一標識蛇位置的是兩個座標,所以這裏visited設置成一個set,存儲當前遍歷過的位置。然後對於蛇的不同狀態(橫 or 豎)根據題意,橫着是隻能順時針轉,豎着時只能逆時針變換,一開始理解錯了,以爲順逆都可以,其實不同
- 然後還有一點就是橫豎時都可以向下和向右走
# 解法1
from collections import deque
class Solution:
def minimumMoves(self, grid: List[List[int]]) -> int:
if not grid:
return 0
n = len(grid)
q = deque([(0, 0, 0)])
used = [[[-1]*n for _ in range(n)] for _ in range(2)]
used[0][0][0] = 0
while q:
p, c, r = q.popleft()
d = used[p][c][r] + 1
if p == 0: # it means vertical look
if r+2 < n and grid[c][r+2] == 0 and used[0][c][r+1] == -1: # one down
used[0][c][r+1] = d
q.append((0, c, r+1))
if c+1 < n and grid[c+1][r] == grid[c+1][r+1] == 0 and used[0][c+1][r] == -1: # two right
used[0][c+1][r] = d
q.append((0, c+1, r))
if c+1 < n and grid[c+1][r] == grid[c+1][r+1] == 0 and used[1][c][r] == -1: # rotate
used[1][c][r] = d
q.append((1, c, r))
else: # horizontal look
if r+1 < n and grid[c][r+1] == grid[c+1][r+1] == 0 and used[1][c][r+1] == -1: # two down
used[1][c][r+1] = d
q.append((1, c, r+1))
if c+2 < n and grid[c+2][r] == 0 and used[1][c+1][r] == -1: # one right
used[1][c+1][r] = d
q.append((1, c+1, r))
if r+1 < n and grid[c][r+1] == grid[c+1][r+1] == 0 and used[0][c][r] == -1: # rotate
used[0][c][r] = d
q.append((0, c, r))
return used[0][n-1][n-2]
# 普通的二維做法
class Solution:
def minimumMoves(self, grid: List[List[int]]) -> int:
if not grid:
return 0
n = len(grid)
visited = set()
q = [(0, 0, 0, 1)]
visited.add((0, 0, 0, 1))
res = -1
target = (n-1, n-2, n-1, n-1)
while q:
res += 1
for i in range(len(q)):
cur = q.pop(0)
if cur == target:
return res
x1, y1, x2, y2 = cur
if x1 == x2: # horizontal look
if y2 + 1 < n and grid[x1][y2+1] == 0: # one right
tmp = (x1, y1+1, x2, y2+1)
if tmp not in visited:
visited.add(tmp)
q.append(tmp)
if x1 + 1 < n and grid[x1+1][y1] == 0 and grid[x1+1][y2] == 0: # two down
tmp = (x1+1, y1, x2+1, y2)
if tmp not in visited:
visited.add(tmp)
q.append(tmp)
if x1 + 1 < n and grid[x1+1][y1] == 0 and grid[x1+1][y1+1] == 0: # clockwise
tmp = (x1, y1, x2+1, y1)
if tmp not in visited:
visited.add(tmp)
q.append(tmp)
else: # vertical look
if y1 + 1 < n and grid[x1][y1+1] == 0 and grid[x2][y2+1] == 0: # two right
tmp = (x1, y1+1, x2, y2+1)
if tmp not in visited:
visited.add(tmp)
q.append(tmp)
if x2 + 1 < n and grid[x2+1][y2] == 0: # one down
tmp = (x1+1, y1, x2+1, y1)
if tmp not in visited:
visited.add(tmp)
q.append(tmp)
if y1 + 1 < n and grid[x1][y1+1] == 0 and grid[x2][y1+1] == 0: # counter clockwise
tmp = (x1, y1, x1, y1+1)
if tmp not in visited:
visited.add(tmp)
q.append(tmp)
return -1