# [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.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.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):
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]]
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.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.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.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.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.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.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.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)
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.2 solution

Method 1: speed pointer

```# 70.79% - 48ms

class Solution(object):
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

return None
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.2 solution

Method 1: double pointer

```# 94.62% - 124ms

class Solution:
# 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:
elif ptrB is None:
# 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.2 solution

Method I:

reference material:

Topics: leetcode