目录
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