Leetcode 2021-11-01

1. Two Sum (Easy)

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

class Solution:
   def twoSum(self, nums: List[int], target: int) -> List[int]:
       dic = dict()
       for i, n in enumerate(nums):
           if target - n in dic:
               return [i, dic[target-n]]
           dic[n] = i

笨方法,double loop, 找到加和是target。O(N**2)
实际解法,用map存位置,如果target-n 剩余的在之前存过的map里,说明 target-n 和 n 就是要求的结果。

2. Add Two Numbers (Medium)

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        
        result = ListNode('NULL')
        head = result
        carry = 0 
        while l1 or l2:
        
            l1val = 0 
            l2val = 0
            if l1:
                l1val = l1.val
                l1 = l1.next
            if l2:
                l2val = l2.val
                l2 = l2.next
            
            val = (l1val+l2val+carry)%10
            carry = (l1val+l2val+carry)//10
            head.next = ListNode(val=val)
            head = head.next
        
        if carry:
            head.next = ListNode(val=carry)
        
        
        return result.next

已经是reverse ordered linked list,所以可以直接对l1,l2的val加和, 但是需要注意carry 以及 l1 or l2 可能在loop到下一位时提前空了, 所以预先定义l1val=0 和l2val=0 有value就overwrite,最后检查是否还有carry, 有就新建个ListNode存carry。返回结果。

3. Longest Substring Without Repeating Characters (Medium)

Given a string s, find the length of the longest substring without repeating characters.

class Solution:
   def lengthOfLongestSubstring(self, s: str) -> int:
       #when meet the overlapping char, start = overlapping char pos end =current char pos -1
       #when not meet overlapping char, start stil old start end = current char pos
       
       if s == ' ': return 1
       if not s: return 0
       result = 0
       dic = dict()
       start = 0
       for i,char in enumerate(s):
           if (char in dic) and (dic[char]>=start):
               #find a overlapping char
               start = dic[char]
               tmp_result = i-start
               result = max(result,tmp_result)
               start = start + 1
           else:
               # no overlapping
               # 0 1 2
               tmp_result = i-start+1
               result = max(result,tmp_result)
               
               
    
           dic[char] = i
       
       return result

花了些时间还是写出答案了,但是时间较长,解题思路:
如果存在已经见过的char,更新结果result = max(result, i-dic[char]),老的已经见过char的下一位是新的起始点, 如果没见过char,则同样更新结果 result = max(result, i-start+1),
但注意 start 这个位置一直应该是单增的, 所以 有这行判断 if (char in dic) and (dic[char]>=start) 例子: abba,如果没这行判断, start 在遇到第二个a时候会重置回0,明显错误。 根据leetcode 这种解法属于sliding window。
精简写法

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        
        dic = dict()
        res = 0
        start = 0
        for i,char in enumerate(s):
            if char in dic:
                start = max(start,dic[char]+1) # a b ..... a  # start=b pos
            res = max(res, i-start+1)
            dic[char] = i
            
        return res

4. Median of Two Sorted Arrays (Hard)

Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays.
The overall run time complexity should be O(log (m+n)).
粗暴解法直接重新排序求中位数,但不满足 O(log (m+n)), 说明用了二分法。
中位数定义是一半的数, 则假设有个分界线 | 同时分开nums1,nums2.
nums1[0] nums1[1] ... nums1[i-1] | nums1[i] ... nums1[m-1]
nums2[0] nums2[1] ... nums2[j-1] | nums2[j] ... nums2[n-1]
median nums1[i-1] 小于 nums2[j]
nums2[j-1] 小于 nums1[i]
i+j = (m+n +1)//2
i: 0~m j = (m+n+1)//2 - i m小于n

在 0~m中 二分查找
确定 i
确定 j
知道 | 边界情况来决定是左移还是右移动搜索边界

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        # nums1[0] nums1[1] ... nums1[i-1] | nums1[i] ... nums1[m-1]
        # nums2[0] nums2[1] ... nums2[j-1] | nums2[j] ... nums2[n-1]
        
        # median    nums1[i-1]<nums2[j]
        #           nums2[j-1]<nums1[i]
        #           i+j =  (m+n +1)//2
        #           i: 0~m  j = (m+n+1)//2 - i     m<n
        
        #edge cases
        if nums1 and not nums2:
            if len(nums1)%2==1:
                return nums1[len(nums1)//2]
            else:
                return  (nums1[len(nums1)//2] +  nums1[len(nums1)//2-1])/2.0
        
        if nums2 and not nums1:
            if len(nums2)%2==1:
                return nums2[len(nums2)//2]
            else:
                return  (nums2[len(nums2)//2] +  nums2[len(nums2)//2-1])/2.0
        
        m = len(nums1)
        n = len(nums2)
        
        if m>n:
            m,n= n,m
            nums1,nums2=nums2,nums1
        
        left = 0
        right = m
        while left <= right:
            
            i = (left+right)//2
            j = (m+n+1)//2 - i
            
            if i-1>=0 and nums1[i-1] > nums2[j]:
                # i is too large
                right = i-1
            elif i<m and nums2[j-1] > nums1[i]:
                #i is too small
                left = i+1
            else:
                
                if i==0:
                    larget_left=nums2[j-1]
                elif j==0:
                    larget_left = nums1[i-1]
                else:
                    larget_left = max(nums2[j-1],nums1[i-1])
                
                if (m+n)%2==1: return larget_left
                
                
                if i==m:
                    smaller_right = nums2[j]
                elif j==n:
                    smaller_right = nums1[i]
                else:
                    smaller_right = min(nums1[i],nums2[j])
                    
                
                return (larget_left+smaller_right)/2.0
                    

直接copy了答案。。。

5. Longest Palindromic Substring (Medium)

Given a string s, return the longest palindromic substring in s.
Input: s = "babad"
Output: "bab"
Note: "aba" is also a valid answer.

#Best Answer
class Solution:
    def longestPalindrome(self, s: str) -> str:
        res = ""
        resLen = 0

        for i in range(len(s)):
            #odd length
            l,r = i,i
            while l>=0 and r<len(s) and s[l]==s[r]:
                if (r-l+1)>resLen:
                    res = s[l:r+1]
                    resLen = r-l+1
                l-=1
                r+=1
            #enven length
            l,r = i,i+1
            while l>=0 and r<len(s) and s[l]==s[r]:
                if (r-l+1)>resLen:
                    res = s[l:r+1]
                    resLen = r-l+1
                
                l-=1
                r+=1
        
        return res


class Solution:
    def longestPalindrome(self, s: str) -> str:
        
        # ith pos, s[i-length]~ s[i] is palindrome 
        #     ith
        #  0 1 2
        #  b a b
        #         or s[i-length-1]~s[i] is palindrome
        
        if not s: return s
        if len(s)==1: return s
        length = 1
        string = s[0]
        
        for i in range(len(s)):
            if (i-length>=0) and (s[i-length:i+1] == s[i-length:i+1][::-1]):
                l = len(s[i-length:i+1])
                if l > length:
                    string = s[i-length:i+1]
                    length = l
                    
            
            if (i-length-1>=0) and s[i-length-1:i+1] == s[i-length-1:i+1][::-1]:
                l = len( s[i-length-1:i+1])
                if  l > length:
                    string = s[i-length-1:i+1]
                    length = l

6. Zigzag Conversion (Medium)

The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)

P A H N
A P L S I I G
Y I R

And then read line by line: "PAHNAPLSIIGYIR"

Write the code that will take a string and make this conversion given a number of rows:

string convert(string s, int numRows);

class Solution:
    def convert(self, s: str, numRows: int) -> str:
        
        if numRows == 1: return s
        
        #init rows
        res = [[] for _ in range(numRows)]
        #
        direction_down = True
        #y_pos is the next y_pos char sitting at
        y_pos = 0
        #
        for char in s:
            res[y_pos].append(char)
            
            if direction_down:
                y_pos += 1
                if y_pos == numRows:
                    #revert pos
                    direction_down = not direction_down
                    y_pos = numRows-1-1
            else:
                y_pos -= 1
                if y_pos == -1:
                    #revert pos
                    direction_down = not direction_down
                    y_pos = 1
                    
        return ''.join([''.join(row) for row in res])

注意numRows==1 edge case

7. Reverse Integer (Medium)

Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the signed 32-bit integer range [-231, 231 - 1], then return 0.

class Solution:
    def reverse(self, x: int) -> int:
        
        sign = 1 if x>=0 else -1
        x = abs(x)
        res = 0
        while x:
            last_dig = x%10
            res = res*10 + last_dig
            x = x//10
            
        return res*sign if (res*sign>=-2**31 and res*sign<=2**31-1) else 0

8. String to Integer (atoi) (Medium)

class Solution:
    def myAtoi(self, s: str) -> int:
        sign = 1
        #1)remove leading white space
        while s and s[0] == ' ':
            s = s[1:]
        #2) check +/-
        if len(s)>1 and s[0] in {'-','+'}:
            s0 = s[0]
            s = s[1:]
            if s0 == '-':
                sign = -1
        #3) read next until next none-dig/end
        i = 0
        while (i<len(s)) and (s[i] in '0123456789')   :
            i+=1
        string = s[:i]
        #4) conver to int
        res = int(string) if string else 0
        #5) clamp res
        res = res*sign
        if res<-2**31:
            res = -2**31
        if res> 2**31-1:
            res = 2**31-1
        
        return res

按照题目要求写每个判断,很easy。

9. Palindrome Number (easy)

Given an integer x, return true if x is palindrome integer.

An integer is a palindrome when it reads the same backward as forward. For example, 121 is palindrome while 123 is not.

class Solution:
    def isPalindrome(self, x: int) -> bool:
        x_backup = x
        if x<0: return False
        res = 0
        while x:
            last_dig = x%10
            res = res*10 + last_dig
            x = x//10
        return res == x_backup
        

10. Regular Expression Matching (Hard)

Given an input string s and a pattern p, implement regular expression matching with support for '.' and '*' where:

'.' Matches any single character.​​​​
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

class Solution:
    @lru_cache(None)
    def isMatch(self, s: str, p: str) -> bool:
        #               .match one
        # p[0] is .   isMatch[s[1:],p[1:]]
        #               *match 0/more p[0]
        # p[1] is *   if * match 0 p[0]
        #               isMatch(s, p[2:])
        #              if * match more p[0]
        #                s[0]==p[0] and isMatch(s[1:],p)
        
        if not p:
            return not s
        
        
        firstmatch = p[0] in {s[0],'.'} if s else False
    
        
        if len(p)>1 and p[1]=='*':
            #start matching
            return (firstmatch and self.isMatch(s[1:],p)) or self.isMatch(s,p[2:])
        else:
            #normal matching
            return firstmatch and self.isMatch(s[1:],p[1:])
        

注意判断 p[0] in {s[0],'.'} if s else False 这里的if s不为空。差一点写出来。。。