Leetcode 2022-03-01

中间搞laser project浪费了不少时间~~~

401. Binary Watch (Easy)

A binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bottom represent the minutes (0-59). Each LED represents a zero or one, with the least significant bit on the right.
Given an integer turnedOn which represents the number of LEDs that are currently on, return all possible times the watch could represent. You may return the answer in any order.

class Solution:
    def readBinaryWatch(self, turnedOn: int) -> List[str]:
        return ['{}:{}'.format(h, str(m).zfill(2))  for h in range(12) for m in range(60) if (bin(h) + bin(m)).count('1') == turnedOn]

#Backtracking way of answer it
class Solution:
    def readBinaryWatch(self, turnedOn: int) -> List[str]:
        res=set()
        nums1=[8,4,2,1]
        nums2=[32,16,8,4,2,1]
        
        
        def generateDigs(nums,count):
            res=set()
            def helper(c,pos,sum_):
                if c==0:
                    res.add(sum_)
                    return
                for i in range(pos,len(nums)):
                    helper(c-1,i+1,sum_+nums[i])
            helper(count,0,0)
            return res
        
        for i in range(turnedOn+1):
            list1=generateDigs(nums1,i)
            list2=generateDigs(nums2,turnedOn-i)
            for num1 in list1:
                if num1>=12: continue
                for num2 in list2:
                    if num2>=60: continue
                    res.add('{}:{}'.format(num1,str(num2).zfill(2)))
        return res


#MY ANSWER
class Solution:
    def readBinaryWatch(self, turnedOn: int) -> List[str]:
      
        res = []


        def process(tmp):
            #print('R',tmp)
            r=''
            h,m = tmp
            if not h:
                r+='0:'
            else:
                r+=str(h)+':'
            
            if not m:
                r+='00'
            else:
                r+= str(m).zfill(2)
            return r
        
        def bt(on_remain,tmp,i,j,v_h,v_m,bebug=''):
            if on_remain<0: return 
            #print(bebug, i,j)
            h = [1,2,4,8]
            m = [1,2,4,8,16,32]
     
            if on_remain == 0:
                res.append(process(tmp))
                return
            if i>=4 and  j>=6: return 
            
            hour = h[i] if i<4 else None
            mini = m[j]  if j<6 else None
            if hour and mini:
                #slect ith hour, j the min
                if hour not in v_h and mini not in v_m:
                    if not (tmp[0]+hour>=12 or tmp[1]+mini>=60): 
                        
                        v_h.add(hour)
                        v_m.add(mini)
                        bt(on_remain-2,[tmp[0]+hour,mini+tmp[1]],i+1,j+1,v_h,v_m,bebug+'H{}M{} '.format(i,j))
                        v_h.remove(hour)
                        v_m.remove(mini)
            
            #select jth min
            if mini:
                #print('m' ,mini,'j',j ,'h',tmp[0])
                if mini not in v_m:
                    if not tmp[1]+mini>=60:  
                        #print('@',mini,on_remain,tmp)
                        v_m.add(mini)
                        bt(on_remain-1,[tmp[0],mini+tmp[1]],i,j+1,v_h,v_m,bebug+'M{} '.format(j))
                        v_m.remove(mini)

            #selet ith hour 
            if hour:
                if hour not in v_h:
                    if not tmp[0]+hour>=12: 
                        v_h.add(hour)
                        bt(on_remain-1,[tmp[0]+hour, tmp[1]],i+1,j,v_h,v_m,bebug+'H{} '.format(i))
                        v_h.remove(hour)
            #skip
            bt(on_remain,tmp.copy(), i+1,j+1,v_h,v_m,bebug+'SKIP{}{} '.format(i,j))

           
        
        bt(turnedOn,[0,0],0,0,set(), set())
        
         
        return list(set(res))

是一个backtracking问题, 写起来难度很大,然而是一个easy问题。。。直接暴力解即可。
但用backtracking就是一个比较难的问题了。注意的是i,j终止条件, if i>=4 or j>=6: return 这个是错的,当i 已经violate时候,j还是能继续+的,eg i=4, j还可以是1,2,3.。。。所以终止条件应该是 if i>=4 and j>=6: return。

402. Remove K Digits (Medium)

Given string num representing a non-negative integer num, and an integer k, return the smallest possible integer after removing k digits from num.

class Solution:
    def removeKdigits(self, num: str, k: int) -> str:
        #
        # 2     1    4
        # left  cur
        # if cur<left, we know left must be removed
        
        stack=[]
        for n in num:
            while k and stack and n<stack[-1]:
                stack.pop()
                k-=1
            stack.append(n)
        
        #truncate remaining k digits at the end
        # if k==0 return entire list
        final_stack=stack[:-k] if k else stack
        
        return ''.join(final_stack).lstrip('0') or '0'

greedy+stack 这个方法第一次比较难想到,需要考虑corner case。

403. Frog Jump (Hard)

A frog is crossing a river. The river is divided into some number of units, and at each unit, there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water.
Given a list of stones' positions (in units) in sorted ascending order, determine if the frog can cross the river by landing on the last stone. Initially, the frog is on the first stone and assumes the first jump must be 1 unit.
If the frog's last jump was k units, its next jump must be either k - 1, k, or k + 1 units. The frog can only jump in the forward direction.

class Solution:
    def canCross(self, stones: List[int]) -> bool:
       
        dp=[False]*len(stones)
        dic={s:i for i,s in enumerate(stones)}
        
        @lru_cache(None)
        def can(ith,k):
            if k<=0: return
            if ith==0:
                dp[0]=True
                if stones[0]+1 in dic:
                    jth=dic[stones[0]+1] 
                    can(jth,1)
            else:
                for i,step in enumerate([k+1,k,k-1]):
                    if stones[ith]+step in dic:
                        stone_ind=dic[stones[ith]+step]
                        dp[stone_ind]=True
                        can(stone_ind,[k+1,k,k-1][i])
            
        can(0,1)
        return dp[-1]

#ANSWER
class Solution:
    def canCross(self, stones: List[int]) -> bool:
        
        dic=dict()
        for s in stones:
            dic[s]=set()
        dic[0].add(0)
        
        for i in range(len(stones)):
            for k in dic[stones[i]]:
                for step in range(k-1,k+2):
                    if step>0 and stones[i]+step in dic:
                        dic[stones[i]+step].add(step)
                        
        return len(dic[stones[-1]])>0

#ANSWER
class Solution:
    def canCross(self, stones: List[int]) -> bool:
        
        seen = set()
        stoneSet = set(stones)
        end = stones[-1]
        stack = [(0, 0)]
        while len(stack) > 0:
            loc, steps = stack.pop()
            if (loc, steps) in seen:
                continue
            seen.add((loc, steps))
            if loc == end:
                return True
            elif loc < end:
                for i in range(steps-1, steps+2):
                    if i <= 0:
                        continue
                    if loc + i in stoneSet:
                        stack.append((loc+i, i))
        return False

#MY ANSWER
class Solution:
    def canCross(self, stones: List[int]) -> bool:
        if len(stones)==0: return True
        if len(stones)>1 and stones[1]-stones[0]!=1: return False
 
        target = stones[-1]
        s = set(stones)

        @lru_cache(None)
        def dfs(cur_pos,k):
            if cur_pos==target:
                return True
         
            res = False
            if cur_pos+k in s:
                res = res or dfs(cur_pos + k, k)
            if cur_pos+k+1 in s:
                res = res or dfs(cur_pos + k+1,k+1)
            if k-1>0 and cur_pos+k-1 in s:
                res =res or dfs(cur_pos+k-1,k-1)
            return res

        return dfs(1,1)

没想到做出来了。。。。。dp with mem。。。
答案更好。答案3思路: each node has 3 children. The goal is to check if we can reach to the end along the edges. We can do it with a Depth First Search with a Hashtable(to avoid redundant calculation)

404. Sum of Left Leaves (Easy)

Given the root of a binary tree, return the sum of all left leaves.

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        if not root: return 0
        if root.left and root.left.left is None and root.left.right is None:
            return root.left.val+self.sumOfLeftLeaves(root.right)
        return  self.sumOfLeftLeaves(root.left)+self.sumOfLeftLeaves(root.right)

405. Convert a Number to Hexadecimal (Easy)

Given an integer num, return a string representing its hexadecimal representation. For negative integers, two’s complement method is used.
All the letters in the answer string should be lowercase characters, and there should not be any leading zeros in the answer except for the zero itself.

class Solution:
    def toHex(self, num: int) -> str:

        li16 = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
        stack = []
        if num<0: 
            num+=0xffffffff+1
        while num>0:
            lastdig = num%16
            stack.append(li16[lastdig])
            num//=16
        
        return ''.join(stack[::-1]) or '0'

406. Queue Reconstruction by Height (Medium)

You are given an array of people, people, which are the attributes of some people in a queue (not necessarily in order). Each people[i] = [hi, ki] represents the ith person of height hi with exactly ki other people in front who have a height greater than or equal to hi.
Reconstruct and return the queue that is represented by the input array people. The returned queue should be formatted as an array queue, where queue[j] = [hj, kj] is the attributes of the jth person in the queue (queue[0] is the person at the front of the queue).

class Solution:
    def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
        people.sort(key = lambda x: (-x[0], x[1]))
        output = []
        for p in people:
            output.insert(p[1], p)
        return output

思路:最高的sort完了,插入次高的,插入位置?

407. Trapping Rain Water II (Hard)

Given an m x n integer matrix heightMap representing the height of each unit cell in a 2D elevation map, return the volume of water it can trap after raining.

class Solution:
    def trapRainWater(self, heightMap: List[List[int]]) -> int:
        
        class Cell:
            def __init__(self,row,col,h):
                self.row=row
                self.col=col
                self.h=h
            
            def __lt__(self,other):
                return self.h<other.h
            
        
        if not heightMap or len(heightMap)==0 or len(heightMap[0])==0:
            return 0
        
        q=[]
        m=len(heightMap)
        n=len(heightMap[0])
        visited=[[False]*n for _ in range(m)]
        #Initially, add all the Cells which are on borders to the queue.
        for i in range(m):
            visited[i][0]=True
            visited[i][n-1]=True
            heapq.heappush(q, Cell(i,0,heightMap[i][0]))
            heapq.heappush(q, Cell(i,n-1,heightMap[i][n-1]))
        for j in range(n):
            visited[0][j]=True
            visited[m-1][j]=True
            heapq.heappush(q, Cell(0,j,heightMap[0][j]))
            heapq.heappush(q, Cell(m-1,j,heightMap[m-1][j]))
        #from the borders, pick the shortest cell visited and check its neighbors: if the neighbor is shorter, collect the water it can trap and update its height as its height plus the water trapped add all its neighbors to the queue.
        dirs = [[-1,0],[1,0],[0,-1],[0,1]]
        res=0
        while q:
            cur_cell=heapq.heappop(q)
            for dir in dirs:
                row=cur_cell.row+dir[0]
                col=cur_cell.col+dir[1]
                if row>=0 and row<m and col>=0 and col<n and not visited[row][col]:
                    visited[row][col]=True
                    res+=max(0,cur_cell.h-heightMap[row][col])
                    heapq.heappush(q,Cell(row,col,max(cur_cell.h,heightMap[row][col])))
                    
        
        return res
                    
#ANSWER
class Solution(object):
    def trapRainWater(self, heightMap):
        if not heightMap or not heightMap[0]:
            return 0
        
        import heapq    
        m, n = len(heightMap), len(heightMap[0])
        heap = []
        visited = [[0]*n for _ in xrange(m)]

        # Push all the block on the border into heap
        for i in xrange(m):
            for j in xrange(n):
                if i == 0 or j == 0 or i == m-1 or j == n-1:
                    heapq.heappush(heap, (heightMap[i][j], i, j))
                    visited[i][j] = 1
        
        result = 0
        while heap:
            height, i, j = heapq.heappop(heap)    
            for x, y in ((i+1, j), (i-1, j), (i, j+1), (i, j-1)):
                if 0 <= x < m and 0 <= y < n and not visited[x][y]:
                    result += max(0, height-heightMap[x][y])
                    heapq.heappush(heap, (max(heightMap[x][y], height), x, y))
                    visited[x][y] = 1
        return result      
        

思路:边界最低的板子是开始点,BFS,如果邻居低说明能存水,做计算,然后埋土。

408. Valid Word Abbreviation (Easy)

A string can be abbreviated by replacing any number of non-adjacent, non-empty substrings with their lengths. The lengths should not have leading zeros.
For example, a string such as "substitution" could be abbreviated as (but not limited to):
"s10n" ("s ubstitutio n")
"sub4u4" ("sub stit u tion")
"12" ("substitution")
"su3i1u2on" ("su bst i t u ti on")
"substitution" (no substrings replaced)
The following are not valid abbreviations:
"s55n" ("s ubsti tutio n", the replaced substrings are adjacent)
"s010n" (has leading zeros)
"s0ubstitution" (replaces an empty substring)
Given a string word and an abbreviation abbr, return whether the string matches the given abbreviation.
A substring is a contiguous non-empty sequence of characters within a string.

class Solution:
    def validWordAbbreviation(self, word: str, abbr: str) -> bool:
        
        dig=0
        while abbr:
            
            if abbr[0].isdigit():
                if dig==0 and abbr[0]=='0': return False
                dig=dig*10+int(abbr[0])
                abbr=abbr[1:]
            else:
                if dig!=0:
                    if len(word)<dig: return False
                    word=word[dig:]
                    dig=0
                else:
                    if not word: return False
                    if abbr[0]!=word[0]: return False
                    abbr=abbr[1:]
                    word=word[1:]
        if dig:
            if len(word)<dig: return False
            if not word: return False
            word=word[dig:]
         
        return not abbr and not word


#MY ANSWER
class Solution:
    def validWordAbbreviation(self, word: str, abbr: str) -> bool:

        while word and abbr:
            dig = ''
            while abbr and abbr[0].isdigit():
                dig+=abbr[0]
                abbr =abbr[1:]
            
            if not dig:
                head = abbr[0]
                if head!=word[0]: return False
                abbr= abbr[1:]
                word =word[1:]
            else:
                if dig[0]=='0': return False
                dig = int(dig)
                if dig>len(word): return False
                word = word[dig:]
        
        return not word and not abbr

409. Longest Palindrome (Easy)

Given a string s which consists of lowercase or uppercase letters, return the length of the longest palindrome that can be built with those letters.
Letters are case sensitive, for example, "Aa" is not considered a palindrome here.

class Solution:
    def longestPalindrome(self, s: str) -> int:
        dic=Counter(s)
        res=0
        for k,v in dic.items():
            res+=v//2*2
            if res%2==0 and v%2==1:
                res+=1
        return res


class Solution:
    def longestPalindrome(self, s: str) -> int:
        c =Counter(s)
        odd = 0
        res = 0
        for k,v in c.items():
            if v%2==0:
                res+=v
            else:
                res+=v-1
                odd = max(v,odd)
        return res+1 if odd else res

找到能组成palindrom的最长组合方式,如果是奇数个,可以变成偶数个,比如bbb只用bb。 但当res%2==0时候,遇到奇数情况可以加1.

odd全变成偶数,但是最大的odd是可以完整加入的, 因为对odd是v-1,所以但凡odd有值,就应该+1.

410. Split Array Largest Sum (Hard)

Given an array nums which consists of non-negative integers and an integer m, you can split the array into m non-empty continuous subarrays.
Write an algorithm to minimize the largest sum among these m subarrays.

#DFS TLE
class Solution:
    def splitArray(self, nums: List[int], k: int) -> int:
        self.res = float('inf')
        def dfs(i,j,cur,max_):
            # i is pointer for nums
            # j is the counter for k
            if i==len(nums):
                if j==k:
                    self.res = min(self.res,max_)
                return
            if i>0:
                dfs(i+1,j,cur+nums[i],max(max_,cur+nums[i]))
            if j<k:
                dfs(i+1,j+1,nums[i],max(max_,nums[i]))
        
        dfs(0,0,0,0)
        return self.res

 
 # TOP DOWN DP

class Solution:
    def splitArray(self, nums: List[int], k: int) -> int:
        n=len(nums)
        prefix_sum = [0]
        for num in nums:
            prefix_sum.append(prefix_sum[-1]+num)
        
        @lru_cache(None)
        def get_min_largest_split_sum(cur_idx, k):
            #we have processd sum before cur_idx
            if k==1:
                return prefix_sum[n]-prefix_sum[cur_idx]
            
            res = prefix_sum[n]
            for split_spot in range(cur_idx, n-k+1):
                # cur_idx, split_spot
                head_sum = prefix_sum[split_spot+1]-prefix_sum[cur_idx]
                tmp = max(head_sum, get_min_largest_split_sum(split_spot+1,k-1))
                res = min(res,tmp)

                if head_sum>=res:
                    break
            return res
        
        return get_min_largest_split_sum(0,k)

#DP  BOTTOM UP
class Solution:
    def splitArray(self, nums: List[int], k: int) -> int:
        n = len(nums)
        memo = [[0] * (k + 1) for _ in range(n)]
        prefix_sum = [0]
        for num in nums:
            prefix_sum.append(prefix_sum[-1]+num)
        
        for subarray_count in range(1, k + 1):
            for curr_index in range(n):
                if subarray_count == 1:
                    memo[curr_index][subarray_count] = prefix_sum[n] - prefix_sum[curr_index]
                    continue 
                minimum_largest_split_sum = prefix_sum[n]
                for i in range(curr_index, n - subarray_count + 1):
                     
                    first_split_sum = prefix_sum[i + 1] - prefix_sum[curr_index]

                   
                    largest_split_sum = max(first_split_sum, memo[i + 1][subarray_count - 1])

                     
                    minimum_largest_split_sum = min(minimum_largest_split_sum, largest_split_sum)

                    if first_split_sum >= minimum_largest_split_sum:
                        break
            
                memo[curr_index][subarray_count] = minimum_largest_split_sum
        
        return memo[0][k]

#Binary Seasrch THIS IS THE BEST METHOD
class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:
        
        def min_subarrays_required(max_sum_allowed: int) -> int:
            current_sum = 0
            splits_required = 0
            
            for element in nums:
                # Add element only if the sum doesn't exceed max_sum_allowed
                if current_sum + element <= max_sum_allowed:
                    current_sum += element
                else:
                    # If the element addition makes sum more than max_sum_allowed
                    # Increment the splits required and reset sum
                    current_sum = element
                    splits_required += 1

            # Return the number of subarrays, which is the number of splits + 1
            return splits_required + 1
        
        # Define the left and right boundary of binary search
        left = max(nums)
        right = sum(nums)
        while left <= right:
            # Find the mid value
            max_sum_allowed = (left + right) // 2
            
            # Find the minimum splits. If splits_required is less than
            # or equal to m move towards left i.e., smaller values
            if min_subarrays_required(max_sum_allowed) <= m:
                right = max_sum_allowed - 1
                minimum_largest_split_sum = max_sum_allowed
            else:
                # Move towards right if splits_required is more than m
                left = max_sum_allowed + 1
        
        return minimum_largest_split_sum
###Binary Seasrch


class Solution(object):
    def splitArray(self, nums, m):
        """
        :type nums: List[int]
        :type m: int
        :rtype: int
        https://leetcode.com/explore/learn/card/binary-search/146/more-practices-ii/1042/discuss/89817/Clear-Explanation:-8ms-Binary-Search-Java?page=1
        """
 
        max_ = sum=0
        for  num in nums:
            max_ = max(num, max_);
            sum += num
            
            
        def valid(target, nums, m):
            count = 1
            total = 0
            for num in nums:
                total += num
                if total > target: 
                    total = num
                    count+=1
                    if count > m:
                        return False
                    
            return True
        
        if m == 1: return sum
        #binary search
        l = max_ 
        r = sum
        while l <= r:
            mid = (l + r)// 2;
            if  valid(mid, nums, m): 
                r = mid - 1 
            else: 
                l = mid + 1
            
        return l


#MY ANSWER
class Solution:
    def splitArray(self, nums: List[int], k: int) -> int:
        l =max(nums)
        r =res = sum(nums)
        
        def allowed(m):
            sum = 0
            split = 0
            for n in nums:
                sum+=n
                if sum>m:
                    sum = n
                    split+=1
            return split+1

        while l<=r:
            m =(l+r)//2

            if allowed(m) <=k:
                # m is larger, that split NO. is small, need shrink
                res = min(res,m)
                r = m-1        
            else:
                l = m+1
        return  res

无思路。。。m=2时候很容易,sort然后找到左右平衡点。m=3,4?
DP很好, bianry search是最优解法。