這次40分鐘做出了前三題,Q1沒注意 kk 可能 比 nn 大吃了一發 WA ;Q2雖然相較Q4的範圍小,但一開始寫得太暴力了吃了 TLE ;Q3很快想到了用分組討論,直接 AC 了;Q4前兩個關鍵點都想到了,只差用兩個限制條件去做統計了,可惜了。

All problems solved by python


Q1: 🟢 2946. Matrix Similarity After Cyclic Shifts 1406

tags: 陣列(Array) 數學(Math) 矩陣(Matrix) 模擬(Simulation)

題意

給定一個下標從 00 開始且大小為 mxnm x n 的整數矩陣 matmat 和整數 kk 。將矩陣中的奇數行循環右移 kk 次,偶數 行循環左移 kk 次。如果初始矩陣和最終矩陣完全相同,則傳回 true ,否則傳回 false 。

思路:模擬

  • 切片模擬即可,注意 kk 可能比 nn 大,所以要取模。
  • 向左移動 kk 次和原本的列相同,等於向右移動 kk 次和原本的列相同,所以不用特別處理左移右移的問題。
1
2
3
4
5
6
7
8
9
class Solution:
def areSimilar(self, mat: List[List[int]], k: int) -> bool:
m, n = len(mat), len(mat[0])
if k > n:
k %= n
for row in mat:
if row[-k:] + row[:-k] != row:
return False
return True

Q2: 🟡 2947. Count Beautiful Substrings I 1451

tags: 字串(String) 前綴和(Prefix Sum) 枚舉(Enumeration)

題意

給定一個字串 ss 和一個正整數 kkss 中的母音字母和子音字母的數量分別用 vowelsvowelsconsonantsconsonants 表示。如果 ss 滿足以下條件,則稱為 beautiful

  1. vowels == consonants,即母音字母和子音字母的數量相等。
  2. (vowels * consonants) \% k == 0 ,即母音字母和子音字母的數量的乘積能被 kk 整除。

返回字串 ssnon-empty beautiful substrings 的數量。

限制:

  • 1<=s.length<=10001 <= s.length <= 1000
  • 1<=k<=10001 <= k <= 1000

思路:前綴和 + 暴力枚舉

直接暴力會 TLE ,用前綴和紀錄母音字母和子音字母的數量,然後枚舉所有子字串,判斷是否符合條件即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution:
def beautifulSubstrings(self, s: str, k: int) -> int:
n = len(s)
ans = 0
vowels = consonants = 0
pre_v = [0] * (n+1)
pre_c = [0] * (n+1)
for i in range(n):
if s[i] in "aeiou":
vowels += 1
else:
consonants += 1
pre_v[i+1] = vowels
pre_c[i+1] = consonants
for i in range(n): # 枚舉起點
for j in range(i + 1, n+1): # 枚舉終點
vowels = pre_v[j] - pre_v[i]
consonants = pre_c[j] - pre_c[i]
if vowels == consonants and vowels * consonants % k == 0:
ans += 1
return ans

Q3: 🟡 2948. Make Lexicographically Smallest Array by Swapping Elements 2047

tags: 分組循環 排序(Sorting) 並查集(Union Find) 互斥集(Disjoint Sets) 陣列(Array)

題意

  • 給定下標從 00 開始的 Array numsnums 和 一個正整數 limitlimit
  • 在每次操作中,可以選擇任兩個下標 iijj,如果滿足 nums[i]nums[j]<=limit|nums[i] - nums[j]| <= limit,則交換 nums[i]nums[i]nums[j]nums[j]
  • 返回執行任意次操作後能得到的 lexicographically smallest array
    • 注意:這裡的字典序是以元素做定義的,例如, [2,10,3][2,10,3] 比陣列 [10,2,3][10,2,3] 字典序更小,下標 0 處是兩個陣列第一個不同的位置,且 2<102 < 10

思路:分組討論(分組循環) + 排序

  • 可以從觀察範例中得出,若一組數字裡面的元素可以互相交換,則這組數字中連續兩個數字的差值一定 limit\leq limit
  • 先把 numsnums 依照數值大小由小到大排序,並記錄原本的下標。
  • 由小到大枚舉 numsnums 中的每個元素,假設當前枚舉到的元素為 numinum_i,則我們要找到所有可以與 numinum_i 交換的元素,也就是差值小於等於 limitlimit 的元素,並將這組數字中的最後一個下標記錄下來。
  • 對於同一組內的元素,我們可以任意交換,所以我們可以把元素小的放在前面,元素大的放在後面,這樣就可以保證字典序最小,這裡用了Min Heap來實現,用排序也可以。
  • 時間複雜度:O(nlogn)O(nlogn)

類題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
def lexicographicallySmallestArray(self, nums: List[int], limit: int) -> List[int]:
n = len(nums)
ans = [0] * n

lst = [(num, idx) for idx, num in enumerate(nums)]
lst.sort()

i = 0
while i < n:
num, idx = lst[i]
hp_idx = []
hp_num = []
heappush(hp_idx, idx)
heappush(hp_num, num)
j = i + 1
while j < n and lst[j][0] - lst[j-1][0] <= limit:
heappush(hp_idx, lst[j][1])
heappush(hp_num, lst[j][0])
j += 1
while hp_idx:
ans[heappop(hp_idx)] = heappop(hp_num)
i = j
return ans

Q4: 🔴 2949. Count Beautiful Substrings II 2445

tags: 質因數分解 雜湊表(Hash Table) 字串(String) 數學(Math) 數論(Number Theory) 前綴和(Prefix Sum)

題意

同 Q2,但是 ss 的長度從 10001000 增加到 51045 * 10^4

  • 1s.length51041 \leq s.length \leq 5 * 10^4
  • 1k10001 \leq k \leq 1000

思路:質因數分解 + 前綴和 + 雜湊表

  • 首先,由於條件1 vowels=consonantsvowels = consonants ,所以當子字串長度為 LL,則 vowels=consonants=L2vowels = consonants = \frac{L}{2}。 故條件2可以改寫為 L24modk=0L2mod4k=0\frac{L^2}{4} \bmod k = 0 \Rightarrow L^2 \bmod 4k =0
  • 4k4k 做質因子分解,得到 4k=p1a1p2a2...pnan4k = p_1^{a_1} * p_2^{a_2} * ... * p_n^{a_n},其中 pip_i 為質數,aia_i 為指數。對於 4k4k 的每個質因數 pip_i,無論aia_i是奇數或偶數,皆滿足 Lmodpiai2=0L \bmod p_i^{\lceil \frac{a_i}{2} \rceil} = 0,故條件2可改寫成 Lmodk2=0L \bmod k_2 = 0,其中 k2=p1a12p2a22...pnan2k_2 = p_1^{\lceil \frac{a_1}{2} \rceil} * p_2^{\lceil \frac{a_2}{2} \rceil} * ... * p_n^{\lceil \frac{a_n}{2} \rceil}
    • 但由於 k1000k \leq 1000 ,可以直接暴力計算出 k2k_2
  • 再來,我們可以令 前綴和 pre_sum[i]pre\_sum[i] 表示 ss 的前 ii 個字元中,母音字母的數量減去輔音字母的數量,或是可以將母音想成 +1+1,子音想成 1-1
  • 若子字串 s[i:j]s[i:j] ( 左閉右開 [i,j)[i, j) ) 為 beautiful ,則需滿足
    1. pre_sum[j]pre_sum[i]=0pre\_sum[j] - pre\_sum[i] = 0,即 s[i:j]s[i:j] 中母音字母和輔音字母的數量相等。
    2. 滿足 Lmodk2=0L \bmod k_2 = 0,即 (ji)modk2=0imodk2=jmodk2(j - i) \bmod k_2 = 0 \Rightarrow i \bmod k_2 = j \bmod k_2
  • 故我們可以枚舉 ii,並用雜湊表紀錄 (imodk2,pre_sum[i])(i \bmod k_2, pre\_sum[i]) 出現的次數,並在枚舉 ii 的同時計算答案。

類題

題單來自:@灵茶山艾府

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution:
def beautifulSubstrings(self, s: str, k: int) -> int:
n = len(s)
# 1. 對 4k 做質因數分解
k = 4 * k
for p in range(int(k**0.5), 1, -1): # k <= 1000
if k % (p ** 2) == 0:
k //= p
break

# 2. 前綴和
vowels_set = set("aeiou")
pre_sum = [0]
for ch in s:
x = 1 if ch in vowels_set else -1
pre_sum.append(pre_sum[-1] + x)

# 3. 雜湊表,統計 (i % k, pre_sum[i]) 出現的次數
ans = 0
cnt = Counter()
for i, s in enumerate(pre_sum):
p = (i % k, s)
ans += cnt[p]
cnt[p] += 1
return ans