[sword finger Offer special assault version] 013 - 026

Posted by designxperts on Tue, 04 Jan 2022 17:24:51 +0100

catalogue

2, 013 - 026

Sword finger Offer II 013 Sum of two-dimensional submatrix

13.1 problem solving

13.2 solution

Sword finger Offer II 014 Anagrams in strings

14.1 problem solving

14.2 solution

Sword finger Offer II 015 All modifiers in the string

15.1 problem solving

15.2 solution

Sword finger Offer II 016

16.1 problem solving

16.2 solution

Sword finger Offer II 017 Shortest string containing all characters ☆

17.1 problem solving

17.2 solution

Sword finger Offer II 018 Valid palindrome

18.1 problem solving

18.2 solution

Sword finger Offer II 019 Delete at most one character to get a palindrome

19.1 problem solving

19.2 solution

Sword finger Offer II 020 Number of palindrome substrings

20.1 problem solving

20.2 solution

Sword finger Offer II 021 Delete the penultimate node of the linked list

21.1 problem solving

21.2 solution

Sword finger Offer II 022

22.1 problem solving

22.2 solution

Sword finger Offer II 023

23.1 problem solving

23.2 solution

2, 013 - 026

Sword finger Offer II 013 Sum of two-dimensional submatrix

13.1 problem solving

13.2 solution

Method 1: one dimensional prefix and

class NumMatrix:
    def __init__(self, matrix: List[List[int]]):
        self.m, self.n = len(matrix), len(matrix[0])
        # Calculate the prefix and of each row - the last column is used as the auxiliary column of column 0
        self.prefix = [[0 for j in range(self.n+1)] for i in range(self.m)]
        for i in range(self.m):
            for j in range(self.n):
                self.prefix[i][j] = self.prefix[i][j-1] + matrix[i][j]

    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        return sum(self.prefix[row][col2]-self.prefix[row][col1-1] for row in range(row1, row2+1))

☆ method 2: two-dimensional prefix and ☆

class NumMatrix:
    def __init__(self, matrix: List[List[int]]):
        m, n = len(matrix), (len(matrix[0]) if matrix else 0)
        self.sums = [[0 for _ in range(n+1)] for _ in range(m+1)]
        for i in range(m):
            for j in range(n):
                # Two plus one minus
                self.sums[i+1][j+1] = self.sums[i][j+1] + self.sums[i+1][j] - self.sums[i][j] + matrix[i][j]

    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        # Two plus one minus
        return self.sums[row2+1][col2+1] - self.sums[row1][col2+1] - self.sums[row2+1][col1] + self.sums[row1][col1]

reference material:

Force buckle

Sword finger Offer II 014 Anagrams in strings

14.1 problem solving

14.2 solution

Method 1: hash mapping sliding window

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        n1, n2 = len(s1), len(s2)
        if n1 > n2:
            return False

        # Take the length of s1 as the initial value of the hash table sliding window key: val = alp: cnt
        hashmap1, hashmap2 = collections.defaultdict(int), collections.defaultdict(int)
        for i in range(n1):
            # newly added
            hashmap1[s1[i]] += 1
            hashmap2[s2[i]] += 1
            # compare
        if hashmap1 == hashmap2:
            return True

        for j in range(n1, n2):
            # Delete left window edge
            hashmap2[s2[j-n1]] -= 1
            if hashmap2[s2[j-n1]] == 0:
                del hashmap2[s2[j-n1]]
            # Add right window edge
            hashmap2[s2[j]] += 1
            # compare
            if hashmap1 == hashmap2:
                return True

        return False

reference material:

Force buckle

Sword finger Offer II 015 All modifiers in the string

15.1 problem solving

15.2 solution

Method 1: hash mapping sliding window

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        # String length
        ns, np = len(s), len(p)
        if ns < np:
            return []

        # Result index list
        res = []

        # Hash map sliding window
        hashmaps, hashmapp = collections.defaultdict(int), collections.defaultdict(int)
        for i in range(np):
            # Add on the right side of the window
            hashmaps[s[i]] += 1
            hashmapp[p[i]] += 1
        # judge
        if hashmaps == hashmapp:
            res.append(0)  # Indexes

        for j in range(np, ns):
            # Delete on the left side of the window
            hashmaps[s[j-np]] -= 1
            if hashmaps[s[j-np]] == 0:
                del hashmaps[s[j-np]]
            # Add on the right side of the window
            hashmaps[s[j]] += 1
            # judge
            if hashmaps == hashmapp:
                res.append(j-np+1)  # Indexes
        
        return res

reference material:

Force buckle

Sword finger Offer II 016

16.1 problem solving

16.2 solution

Method 1: double pointer + hash mapping

# 98.88% - 44ms

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        max_len = 0   # Maximum non repeating substring length
        hashmap = {}  # key: val = char: idx
        lhs = -1      # The left pointer to the substring is not currently repeated

        for rhs in range(len(s)):
            cur = hashmap.get(s[rhs])
            # Update lhs construct new left pointer position
            if (cur is not None) and (cur > lhs):
                lhs = cur
            # The new index of new or updated s[rhs] is rhs
            hashmap[s[rhs]] = rhs
            # Update maximum length
            max_len = max(max_len, rhs-lhs)
            
        return max_len
# 98.88% - 44ms - script 2

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        max_len = 0   # Maximum non repeating substring length
        hashmap = {}  # key: val = char: idx
        lhs = -1      # The left pointer of the substring is not currently repeated

        for rhs, char in enumerate(s):
            if (char in hashmap) and (hashmap[char] > lhs):
                # Update lhs construct new left pointer position
                lhs = hashmap[char]
                # Add or update the new index of char to rhs
                hashmap[char] = rhs
            else:
                # The new index of new or updated s[rhs] is rhs
                hashmap[char] = rhs
                # Update maximum length
                max_len = max(max_len, rhs-lhs)
            
        return max_len

reference material:

Force buckle

Sword finger Offer II 017 Shortest string containing all characters ☆

17.1 problem solving

17.2 solution

Method 1: double pointer + hash sliding window

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        # The length can never be satisfied
        if len(s) < len(t):
            return ""
        
        left, right = 0, 0         # window left and right index
        min_index = [0, len(s)]    # Minimum length index
        isFind = False             # Have you ever found a string tag that meets the requirements
        numValid = 0               # value key (alp) counter satisfied ☆

        # Initialize requirements dictionary needs
        needs = collections.Counter(t)  # More convenient
        # needs = {}
        # for alp in t:
        #     if needs.get(alp) is None:
        #         needs[alp] = 0
        #     needs[alp] += 1
 
        # Initialize the window (meet the requirements) dictionary window - only use the keys of needs to avoid recording other useless keys
        window = {key: 0 for key in needs.keys()}
        
        # iteration 
        while right < len(s):
            new_alp = s[right]     # Get new character
            right += 1             # Move right border right
            if window.get(new_alp) is not None:        # window gets a
                window[new_alp] += 1 
                if window[new_alp] == needs[new_alp]:  # Satisfy a certain clause
                    numValid += 1
            
            # Once the window covers all needs - the premise of moving the left boundary to the right ☆
            while numValid == len(needs):
                isFind = True      # find
                
                # Record the shortest length - both closed on the left and open on the right, so it is 1 less than the actual length
                if right-left < min_index[1]-min_index[0]:
                    min_index = [left, right]
                
                old_alp = s[left]  # Remove old characters
                left += 1          # Move left boundary right
                # Keys not from needs do not affect or operate
                if window.get(old_alp) is not None:              # window remove a
                    window[old_alp] = max(window[old_alp]-1, 0)  # At least 0
                    if window[old_alp] < needs[old_alp]:         # Do not meet a certain requirement
                        numValid -= 1
        
        # s(left: right)
        return s[min_index[0]: min_index[1]] if isFind else ""

reference material:

Force buckle

Sword finger Offer II 018 Valid palindrome

18.1 problem solving

18.2 solution

Method 1: cleaning + traversal

# 98.90% - 32ms

class Solution:
    def isPalindrome(self, s: str) -> bool:
        # 1. Data cleaning
        tmp = [c.lower() for c in s if c.isalnum()]  # isalnum is equivalent to isalpha or isdigit
        # 2. Traversal comparison
        n = len(tmp)
        for i in range(n // 2):
            if tmp[i] != tmp[n-1-i]:
                return False
        return True

reference material:

Force buckle

Sword finger Offer II 019 Delete at most one character to get a palindrome

19.1 problem solving

19.2 solution

Method 1: delete the matching of characters on the left and right respectively

# 80.81% - 124ms

class Solution:
    def validPalindrome(self, s: str) -> bool:
        def match(lhs, rhs):
            # Traverse the matching from both ends to the center one by one
            while lhs < rhs and s[lhs] == s[rhs]:
                lhs += 1
                rhs -= 1
            return lhs, rhs

        # Normal matching
        lhs, rhs = match(0, len(s)-1)
        if lhs >= rhs:
            return True

        # Delete the currently mismatched left character
        lhs1, rhs1 = match(lhs+1, rhs)
        if lhs1 >= rhs1:
            return True    

        # Delete the currently mismatched right character
        lhs2, rhs2 =  match(lhs, rhs-1)
        if lhs2 >= rhs2:
            return True
        
        # Still do not match
        return False

reference material:

Force buckle

Sword finger Offer II 020 Number of palindrome substrings ☆

20.1 problem solving

20.2 solution

Method 1: dynamic programming

# 50% - 196ms

class Solution:
    def countSubstrings(self, s: str) -> int:
        res = 0
        n = len(s)

        # dp[i][j] indicates whether s[i, j] is a palindrome string
        dp = [[False for _ in range(n)] for _ in range(n)]

        for i in range(n-1, -1, -1):
            for j in range(i, n):
                # state transition 
                if s[i] == s[j] and (j - i < 2 or dp[i+1][j-1] is True):
                    dp[i][j] = True 
                    res += 1
        return res

Method 2: Manacher algorithm (horse drawn cart)

# 99.46% - 40ms

class Solution:
    def countSubstrings(self, s: str) -> int:
        # Horse drawn cart algorithm (Manacher) 
        # https://leetcode-cn.com/problems/a7VOhD/solution/hui-wen-zi-zi-fu-chuan-de-ge-shu-by-leet-ejfv/

        # Insert between all adjacent characters # (placeholders at the beginning and end) to ensure that all palindrome strings found are of odd length
        t = ["$", "#"]
        for c in s:
            t.append(c)
            t.append("#")
        n = len(t)
        t.append("!")

        # f[i] represents the maximum palindrome radius of extension Exhibition with the ith bit of t as the palindrome center
        f = [1 for _ in range(n)]
        imax = rmax = res = 0
        # Enumerate each position i of t and first assume that i is the palindrome center
        # If f[i] is known, the right endpoint rmax of I is i + f[i] - 1
        for i in range(1, n):  # Note that i starts from 1
            # Initialize f[i]
            # If i < = Rmax, it indicates that the current i is included in the current maximum palindrome substring
            # Suppose J is the symmetric position of I about the palindrome center IMAX of this maximum palindrome (i.e. j+i = 2) × imax)
            # f[i] is at least equal to min{f[j], rmax − i+1} = min(f[2*imax-i], rmax-i+1)
            # If f[i] and rmax − i+1 are taken as small, it is necessary to ensure that the palindrome string is within the current maximum palindrome string
            if i <= rmax:
                f[i] = min(rmax-i+1, f[2*imax-i]) 
            # Center expansion
            while t[i+f[i]] == t[i-f[i]]:
                f[i] += 1
            # Maintain imax and rmax dynamically
            if i + f[i] - 1 > rmax:
                imax = i             # i with the largest rmax is imax
                rmax = i + f[i] - 1  # Update larger rmax
            # Statistical answer, the current contribution is rounded up to (f[i] - 1) / 2
            res += (f[i] // 2)

        return res

reference material:

Force buckle

Sword finger Offer II 021 Delete the penultimate node of the linked list

21.1 problem solving

21.2 solution

Method 1: speed pointer

# 98.84% - 24ms

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        # Pseudo head - sentinel node - easy to delete head node
        dummy = ListNode(-1)
        dummy.next = head
        lhs = rhs = head  # Speed pointer
        lcnt = rcnt = 1   # Speed counter 
        while rhs.next:
            # Slow pointer
            lhs = lhs.next
            lcnt += 1
            # Quick pointer
            rhs = rhs.next
            rcnt += 1
            if rhs.next:
                rhs = rhs.next
                rcnt += 1
        # Length of the second half
        diff = rcnt - lcnt
        # Starting from the midpoint (second half)
        if n <= diff:
            tmp = lhs
            step = diff - n
        # Starting from the starting point (first half)
        else:
            tmp = dummy
            step = rcnt - n
        # Move and jump delete
        for _ in range(step):
            tmp = tmp.next
        tmp.next = tmp.next.next if tmp.next else None

        return dummy.next

reference material:

Force buckle

Sword finger Offer II 022 The entry node of the link in the linked list ☆

22.1 problem solving

22.2 solution

Method 1: speed pointer

# 70.79% - 48ms

class Solution(object):
    def getIntersect(self, head):
        slow = head                # Slow pointer slow = tortoise
        fast = head                # fast pointer = Rabbit hare
        while fast and fast.next:
            slow = slow.next       # One step at a time
            fast = fast.next.next  # Take two steps at a time
            if slow == fast:       # If there is a first encounter node, it is proved that there is a ring
                return slow
        return None

    def detectCycle(self, head):
        # Empty linked list
        if not head:
            return None
        # Acyclic linked list
        intersect = self.getIntersect(head)
        if not intersect: 
            return None
        # To find the entrance to the cycle, we have two pointers traverse at the same speed
        # one from the front of the list, and the other from the point of intersection.
        ptr1 = head           # Starting from the starting point of the linked list
        ptr2 = intersect      # Starting from the meeting node ☆
        while ptr1 != ptr2:   # The second encounter node is the ring entry node
            ptr1 = ptr1.next  # One step at a time
            ptr2 = ptr2.next  # One step at a time
        return ptr1

reference material:

Force buckle

Sword finger Offer II 023 The first coincident node of two linked lists

23.1 problem solving

23.2 solution

Method 1: double pointer

# 94.62% - 124ms

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        ptrA, ptrB = headA, headB
        # If and only if ptrA and ptrB are None at the same time, there is no coincidence
        # The following order is immutable!!
        while ptrA or ptrB:
            # 1. Starting point of first exchange
            if ptrA is None:
                ptrA = headB
            elif ptrB is None:
                ptrB = headA
            # 2. Judge coincidence node
            if ptrA is ptrB:
                return ptrA
            # 3. Take a step
            ptrA = ptrA.next
            ptrB = ptrB.next
        return None

reference material:

Force buckle

Sword finger Offer II 024

24.1 problem solving

24.2 solution

Method I:

reference material:

Topics: leetcode