【周賽】第156場-2019-9-29

目錄

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 twith 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 and t 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

 

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