diff --git "a/content/docs/career/interview-prep/leetcode/[1545]\346\211\276\345\207\272\347\254\254 N \344\270\252\344\272\214\350\277\233\345\210\266\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254 K \344\275\215.md" b/content/docs/career/interview-prep/leetcode/1545-find-kth-bit-in-nth-binary-string.zh.md similarity index 100% rename from "content/docs/career/interview-prep/leetcode/[1545]\346\211\276\345\207\272\347\254\254 N \344\270\252\344\272\214\350\277\233\345\210\266\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254 K \344\275\215.md" rename to content/docs/career/interview-prep/leetcode/1545-find-kth-bit-in-nth-binary-string.zh.md diff --git "a/content/docs/career/interview-prep/leetcode/2131. \350\277\236\346\216\245\344\270\244\345\255\227\346\257\215\345\215\225\350\257\215\345\276\227\345\210\260\347\232\204\346\234\200\351\225\277\345\233\236\346\226\207\344\270\262.md" b/content/docs/career/interview-prep/leetcode/2131-longest-palindrome-by-concatenating-two-letter-words.zh.md similarity index 98% rename from "content/docs/career/interview-prep/leetcode/2131. \350\277\236\346\216\245\344\270\244\345\255\227\346\257\215\345\215\225\350\257\215\345\276\227\345\210\260\347\232\204\346\234\200\351\225\277\345\233\236\346\226\207\344\270\262.md" rename to content/docs/career/interview-prep/leetcode/2131-longest-palindrome-by-concatenating-two-letter-words.zh.md index 6e9953a..14622e5 100644 --- "a/content/docs/career/interview-prep/leetcode/2131. \350\277\236\346\216\245\344\270\244\345\255\227\346\257\215\345\215\225\350\257\215\345\276\227\345\210\260\347\232\204\346\234\200\351\225\277\345\233\236\346\226\207\344\270\262.md" +++ b/content/docs/career/interview-prep/leetcode/2131-longest-palindrome-by-concatenating-two-letter-words.zh.md @@ -1,5 +1,5 @@ --- -title: 2131. 连接两字母单词得到的最长回文串.md +title: "2131. 连接两字母单词得到的最长回文串" description: "LeetCode 2131. 连接两字母单词得到的最长回文串题解 — 使用哈希表统计单词出现次数,通过检查当前单词的反向是否存在来配对回文对,最后判断是否有自回文单词作为中心。适合准备算法面试、刷 LeetCode 哈希表与回文串题型的同学阅读。" date: "2025/5/25-2:33" tags: diff --git "a/content/docs/career/interview-prep/leetcode/2131. \350\277\236\346\216\245\344\270\244\345\255\227\346\257\215\345\215\225\350\257\215\345\276\227\345\210\260\347\232\204\346\234\200\351\225\277\345\233\236\346\226\207\344\270\262.en.md" "b/content/docs/career/interview-prep/leetcode/2131. \350\277\236\346\216\245\344\270\244\345\255\227\346\257\215\345\215\225\350\257\215\345\276\227\345\210\260\347\232\204\346\234\200\351\225\277\345\233\236\346\226\207\344\270\262.en.md" deleted file mode 100644 index 381ec97..0000000 --- "a/content/docs/career/interview-prep/leetcode/2131. \350\277\236\346\216\245\344\270\244\345\255\227\346\257\215\345\215\225\350\257\215\345\276\227\345\210\260\347\232\204\346\234\200\351\225\277\345\233\236\346\226\207\344\270\262.en.md" +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: "2131. Longest Palindrome by Concatenating Two Letter Words" -description: "LeetCode 2131. Longest Palindrome by Concatenating Two Letter Words — solution using a hash map to count word occurrences, pairing words with their reverses to form palindrome pairs, and checking for any self-palindrome word that can serve as the center. Great for interview prep on hash tables and palindrome problems." -date: "2025/5/25-2:33" -tags: - - - Python - - - Answer -abbrlink: 9fa195e5 -docId: ksw2vic4alf1tdnnueay81g8 -lang: en -translatedFrom: zh -translatedAt: 2026-05-11T00:00:00Z -translatorAgent: claude-sonnet-4-6 ---- - -# QUESTION - -[2131. Longest Palindrome by Concatenating Two Letter Words](https://leetcode.cn/problems/longest-palindrome-by-concatenating-two-letter-words/description/?envType=daily-question&envId=2025-05-25) - -# My Think - -The idea comes from the brilliant Ling-nc. - -We build a hash map to count the occurrences of each word. - -For each word, we check whether its reverse exists in the hash map. -If it does, they can form a palindrome pair — we update the result and decrease the corresponding count. -If the reverse does not exist, we increment the count for the current word. -Finally, we check if there's any palindromic word that can be used as the center of the palindrome. - -Example: - -1. Input: ["lc", "cl", "gg", "gg"] - -2. "lc" has no pair → stored in the map: { "lc": 1 } - -3. "cl" finds "lc" exists → use "lc" + "cl" as a pair → res += 4 → map updated to { "lc": 0 } - -4. "gg" has no pair → stored in the map: { "lc": 0, "gg": 1 } - -5. Second "gg" finds "gg" exists → use "gg" + "gg" as a pair → res += 4 → map becomes { "lc": 0, "gg": 0 } - - Then we check whether the hash map contains any palindromic word (i.e., a word with two identical characters) that can be used as the center of the final palindrome string. - If such a word exists, we can add 2 more to the result. - -6. Finding a center word (can only pick one symmetric word) - ⚠️ In this example, all "gg" words have been paired, so none is left → no center word is added. - -# Code - -```python -from collections import defaultdict -from typing import List -class Solution: - def longestPalindrome(self, words: List[str]) -> int: - count = defaultdict(int) - res = 0 - for word in words: - if count[word[::-1]] > 0: - count[word[::-1]] -= 1 - res += 4 - else: - count[word] += 1 - for key, value in count.items(): - if key[0] == key[1] and value > 0: - res += 2 - break - return res -``` - -```typescript -function longestPalindrome(words: string[]): number { - const count: Map = new Map(); - let res = 0; - - for (const word of words) { - const reversed = word.split("").reverse().join(""); - const reversedCount = count.get(reversed) ?? 0; - - if (reversedCount > 0) { - count.set(reversed, reversedCount - 1); - res += 2 * word.length; - } else { - count.set(word, (count.get(word) ?? 0) + 1); - } - } - - for (const [word, freq] of count.entries()) { - if (word === word.split("").reverse().join("") && freq > 0) { - res += word.length; - break; // 只能选一个居中的回文串 - } - } - - return res; -} -``` diff --git a/content/docs/career/interview-prep/leetcode/2241. Design an ATM Machine.en.md b/content/docs/career/interview-prep/leetcode/2241. Design an ATM Machine.en.md deleted file mode 100644 index e708ca7..0000000 --- a/content/docs/career/interview-prep/leetcode/2241. Design an ATM Machine.en.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: "2241. Design an ATM Machine" -description: "LeetCode 2241. Design an ATM Machine — simulating bank ATM deposit and withdrawal logic with a focus on implementing a greedy withdrawal strategy: always use the largest denomination first (500, 200, 100, 50, 20), and handle cases where the balance is insufficient or cannot be evenly dispensed. Suitable for algorithm learners preparing for system design interviews or practicing object-oriented simulation problems." -date: "2025-01-06" -tags: - - - Python - - - Answer -abbrlink: a21411f -docId: lzrh7ftq3kegsyx8gimonrfu -lang: en -translatedFrom: zh -translatedAt: 2026-05-11T00:00:00Z -translatorAgent: claude-sonnet-4-6 ---- - -# QUESTION - -[2241. Design an ATM Machine](https://leetcode.cn/problems/design-an-atm-machine/description/?envType=daily-question&envId=2025-01-05) - -There is an ATM machine that stores banknotes of 5 denominations: 20, 50, 100, 200, and 500 dollars. Initially the ATM is empty. The user can use the machine to deposit or withdraw any amount of money. - -When withdrawing, the machine prioritizes using banknotes of larger values. - -For example, if you want to withdraw $300 and there are 2 $50 banknotes, 1 $100 banknote, and 1 $200 banknote, then the machine will use the $100 and $200 banknotes. -However, if you try to withdraw $600 and there are 3 $200 banknotes and 1 $500 banknote, then the withdraw request will be rejected because the machine will first try to use the $500 banknote and then be unable to use banknotes to complete the remaining $100. Note that the machine is not allowed to use the $200 banknotes instead of the $500 banknote. - -Implement the ATM class: - -ATM() Initializes the ATM object. -void deposit(int[] banknotesCount) Deposits new banknotes in the order $20, $50, $100, $200, and $500. -int[] withdraw(int amount) Returns an array of length 5 of the number of banknotes that will be handed to the user in the order $20, $50, $100, $200, and $500, and update the number of banknotes in the ATM after withdrawing. Returns [-1] if it is not possible (do not withdraw any banknotes in this case). - -# My Think - -The purpose of this question is to simulate an ATM machine, which returns the amount of money you withdraw, no more and no less. I am greedy, because the second sentence "There are 3 `$200` bills and 1 `$500` bill in the machine, so the withdrawal request will be rejected" -This shows that we can skip thinking about the knapsack problem in complex dynamic programming and directly consider simple greed. - -Because the denominations of the deposited money are only `20`, `50`, `100`, `200`, `500`, we can store them in the list in advance and wait for traversal. - -Then we create a `defaultdict()` to create a hash table for each denomination in the ATM machine. - -`deposit()` creates a reverse traversal dictionary. Because we need to traverse the dictionary from large denominations to small denominations, the reverse dictionary is very convenient at this time. - -Assuming the initial `amount` is `600`, the first denomination traversed is `500`, It fits the logic of the question very well. - -For the `withdraw()` function, I created a temporary dictionary deep copy storage so that the initial array will not be changed when `[-1]` is returned. Otherwise, it will be troublesome to backtrack. - -Here, Sylvia and I used two different traversal methods. She traversed the list of denominations, while I traversed the dictionary directly (actually traversed the key directly). - -1. If the current denomination (`600`) is greater than the current denomination (`500`), then try to deduct it. If the bank money is withdrawn directly, then look at the next denomination. - -2. If it is not withdrawn, then `amount` deducts the deductible share and then continues to look at the next denomination. - -3. If there is still `amount` left at the end, return `[-1]`, otherwise calculate how many bills have been consumed in total, which is the answer. - -# Code - -```python -import copy -from typing import List - -from collections import defaultdict - - -class ATM: - - def __init__(self): - self.sd = defaultdict(int) - self.amount = ['20', '50', '100', '200', '500'] - - def deposit(self, banknotesCount: List[int]) -> None: - for i in range(len(banknotesCount) - 1, -1, -1): - self.sd[self.amount[i]] += banknotesCount[i] - - - - def withdraw(self, amount: int) -> List[int]: - tempSd = copy.deepcopy(self.sd) - # key = 面值, value = 张数 - for key, value in tempSd.items(): - if amount >= int(key) and value > 0: - # 需要多少张钞票 - howManyPiece = amount // int(key) - if howManyPiece >= value: - # 全部取出来 - tempSd[key] = 0 - amount -= value * int(key) - else: - # 取出这么多钞票 - tempSd[key] -= howManyPiece - amount -= int(key) * howManyPiece - else: - if amount > 0: - return [-1] - else: - ans = [] - for i in self.sd.keys(): - ans.append(self.sd[i] - tempSd[i]) - self.sd = copy.deepcopy(tempSd) - return ans[::-1] -``` diff --git a/content/docs/career/interview-prep/leetcode/2241. Design an ATM Machine.md b/content/docs/career/interview-prep/leetcode/2241. Design an ATM Machine.md deleted file mode 100644 index d296e39..0000000 --- a/content/docs/career/interview-prep/leetcode/2241. Design an ATM Machine.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -title: 2241. Design an ATM Machine.md -description: "LeetCode 2241. Design an ATM Machine 题解 — 模拟银行 ATM 的存款与取款逻辑,重点实现贪心取款策略:优先使用大面额钞票(500、200、100、50、20),并处理余额不足或无法凑整时的拒绝场景。适合准备系统设计面试或练习面向对象模拟题的算法学习者。" -date: "2025-01-06" -tags: - - - Python - - - Answer -abbrlink: a21411f -docId: lzrh7ftq3kegsyx8gimonrfu ---- - -# QUESTION: - -[2241. Design an ATM Machine.md](https://leetcode.cn/problems/design-an-atm-machine/description/?envType=daily-question&envId=2025-01-05) -There is an ATM machine that stores banknotes of 5 denominations: 20, 50, 100, 200, and 500 dollars. Initially the ATM is empty. The user can use the machine to deposit or withdraw any amount of money. - -When withdrawing, the machine prioritizes using banknotes of larger values. - -For example, if you want to withdraw $300 and there are 2 $50 banknotes, 1 $100 banknote, and 1 $200 banknote, then the machine will use the $100 and $200 banknotes. -However, if you try to withdraw $600 and there are 3 $200 banknotes and 1 $500 banknote, then the withdraw request will be rejected because the machine will first try to use the $500 banknote and then be unable to use banknotes to complete the remaining $100. Note that the machine is not allowed to use the $200 banknotes instead of the $500 banknote. -Implement the ATM class: - -ATM() Initializes the ATM object. -void deposit(int[] banknotesCount) Deposits new banknotes in the order $20, $50, $100, $200, and $500. -int[] withdraw(int amount) Returns an array of length 5 of the number of banknotes that will be handed to the user in the order $20, $50, $100, $200, and $500, and update the number of banknotes in the ATM after withdrawing. Returns [-1] if it is not possible (do not withdraw any banknotes in this case). - -# My Think: - -The purpose of this question is to simulate an ATM machine, which returns the amount of money you withdraw, no more and no less. I am greedy, because the second sentence "There are 3 `$200` bills and 1 `$500` bill in the machine, so the withdrawal request will be rejected" -This shows that we can skip thinking about the knapsack problem in complex dynamic programming and directly consider simple greed. - -Because the denominations of the deposited money are only `20`, `50`, `100`, `200`, `500`, we can store them in the list in advance and wait for traversal. - -Then we create a `defaultdict()` to create a hash table for each denomination in the ATM machine. - -`deposit()` creates a reverse traversal dictionary. Because we need to traverse the dictionary from large denominations to small denominations, the reverse dictionary is very convenient at this time. - -Assuming the initial `amount` is `600`, the first denomination traversed is `500`, It fits the logic of the question very well - -For the `withdraw()` function, I created a temporary dictionary deep copy storage so that the initial array will not be changed when `[-1]` is returned. Otherwise, it will be troublesome to backtrack. - -Here, Sylvia and I used two different traversal methods. She traversed the list of denominations, while I traversed the dictionary directly (actually traversed the key directly). - -1. If the current denomination (`600`) is greater than the current denomination (`500`), then try to deduct it. If the bank money is withdrawn directly, then look at the next denomination. - -2. If it is not withdrawn, then `amount` deducts the deductible share and then continues to look at the next denomination. - -3. If there is still `amount` left at the end, return `[-1]`, otherwise calculate how many bills have been consumed in total, which is the answer. - -这道题的目的是模拟一台ATM机器, 让你取多少钱, 就返回多少钱, 不能多也不能少. 我是贪心的思想, 因为第二句"机器里有 3 张 `$200` 的钞票和1 张 `$500` 的钞票,那么取款请求会被拒绝" -这就说明我们可以跳过思考复杂的动态规划中的背包问题, 而直接考虑简单的贪心. - -因为存的钱的面额只有`20`, `50`, `100`, `200`, `500` 这五种面额, 于是我们提前存在列表里面等待遍历即可. -然后我们创建一个`defaultdict()`, 为ATM机器里面的每种面额创建哈希表. - -`deposit()`创建了一个反向遍历的字典. 因为我们需要从大面额到小面额遍历字典, 在这个时候反向的字典就很方便. - - 假设初始`amount`为`600`, 遍历到的第一个面额就是`500`, 就很符合题目的逻辑 - -`withdraw()`函数, 我之所以创建了一个临时字典深拷贝储存是在返回`[-1]`的情况下不更改初始数组. 否则还要回溯挺麻烦的. - -这里我和Sylvia用了两种不同的遍历方式, 她遍历了面额的列表, 而我是直接遍历的字典(实际上直接遍历的key). - -1. 如果当前面额(`600`)大于了当前面额(`500`), 那就尝试扣除, 如果直接把银行钱取完了, 那再看下一个面值. -2. 如果没有取完, 那么`amount`扣除掉能扣除的份额, 再继续看下一个面额即可. -3. 到最后`amount`还有剩余则返回`[-1]`, 否则计算一共消耗了多少张钞票, 即是答案了. - -# Code: - -```python -import copy -from typing import List - -from collections import defaultdict - - -class ATM: - - def __init__(self): - self.sd = defaultdict(int) - self.amount = ['20', '50', '100', '200', '500'] - - def deposit(self, banknotesCount: List[int]) -> None: - for i in range(len(banknotesCount) - 1, -1, -1): - self.sd[self.amount[i]] += banknotesCount[i] - - - - def withdraw(self, amount: int) -> List[int]: - tempSd = copy.deepcopy(self.sd) - # key = 面值, value = 张数 - for key, value in tempSd.items(): - if amount >= int(key) and value > 0: - # 需要多少张钞票 - howManyPiece = amount // int(key) - if howManyPiece >= value: - # 全部取出来 - tempSd[key] = 0 - amount -= value * int(key) - else: - # 取出这么多钞票 - tempSd[key] -= howManyPiece - amount -= int(key) * howManyPiece - else: - if amount > 0: - return [-1] - else: - ans = [] - for i in self.sd.keys(): - ans.append(self.sd[i] - tempSd[i]) - self.sd = copy.deepcopy(tempSd) - return ans[::-1] -``` diff --git a/content/docs/career/interview-prep/leetcode/2270. Number of Ways to Split Array.en.md b/content/docs/career/interview-prep/leetcode/2270. Number of Ways to Split Array.en.md deleted file mode 100644 index b1b3905..0000000 --- a/content/docs/career/interview-prep/leetcode/2270. Number of Ways to Split Array.en.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: "2270. Number of Ways to Split Array" -description: "LeetCode 2270. Number of Ways to Split Array — solution using prefix sums and a single traversal to compute the total number of valid array splits. The key technique is maintaining left and right subarray sums and comparing them dynamically. Suitable for algorithm learners preparing for interviews and looking to reinforce prefix sum skills." -date: "2025/1/14-9:31" -tags: - - - Python - - - Answer -abbrlink: c25bb550 -docId: a6inw303oslb7i5tcqj5xxx4 -lang: en -translatedFrom: zh -translatedAt: 2026-05-11T00:00:00Z -translatorAgent: claude-sonnet-4-6 ---- - -# QUESTION - -[2270. Number of Ways to Split Array](https://leetcode.cn/problems/number-of-ways-to-split-array/description/) - -# My Think - -`2 <= nums.length <= 10^5`, so we can directly take the first element. The initial state has the pointer at index 0, about to move to index 1. A single `for` loop is all we need. - -The second method is the key one — taken from the editorial. - -# Code - -```python -class Solution: - def waysToSplitArray(self, nums: List[int]) -> int: - temp_sum = nums[0] - total_sum = sum(nums) - temp_sum - ans = 0 - for i in range(1, len(nums)): - if temp_sum >= total_sum: - ans += 1 - temp_sum += nums[i] - total_sum -= nums[i] - return ans -``` - -```python -t = (sum(nums) + 1) // 2 -return sum(s >= t for s in accumulate(nums[:-1])) -``` diff --git a/content/docs/career/interview-prep/leetcode/2270. Number of Ways to Split Array.md b/content/docs/career/interview-prep/leetcode/2270. Number of Ways to Split Array.md deleted file mode 100644 index d67502f..0000000 --- a/content/docs/career/interview-prep/leetcode/2270. Number of Ways to Split Array.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: 2270. Number of Ways to Split Array.md -description: "LeetCode 2270. Number of Ways to Split Array 题解 — 使用前缀和与一次遍历计算数组分割方式总数,关键技巧是维护左右子数组和并动态比较,适合准备面试、刷题巩固数组与前缀和技巧的算法学习者。" -date: "2025/1/14-9:31" -tags: - - - Python - - - Answer -abbrlink: c25bb550 -docId: a6inw303oslb7i5tcqj5xxx4 ---- - -# QUESTION: - -[2270. Number of Ways to Split Array.md](https://leetcode.cn/problems/number-of-ways-to-split-array/description/) - -# My Think: - -`2 <= nums.length <= 105`, 因此我们可以直接获取到第一个数字, 初始状态就在指针于index0, 正要往index1走的时候. -然后只需要一次For循环就可以搞定 - -重点是第二个方法, 来自题解. - -# Code: - -```python -class Solution: - def waysToSplitArray(self, nums: List[int]) -> int: - temp_sum = nums[0] - total_sum = sum(nums) - temp_sum - ans = 0 - for i in range(1, len(nums)): - if temp_sum >= total_sum: - ans += 1 - temp_sum += nums[i] - total_sum -= nums[i] - return ans -``` - -```python -t = (sum(nums) + 1) // 2 -return sum(s >= t for s in accumulate(nums[:-1])) -``` diff --git a/content/docs/career/interview-prep/leetcode/3138. Minimum Length of Anagram Concatenation.en.md b/content/docs/career/interview-prep/leetcode/3138. Minimum Length of Anagram Concatenation.en.md deleted file mode 100644 index 6a039bd..0000000 --- a/content/docs/career/interview-prep/leetcode/3138. Minimum Length of Anagram Concatenation.en.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: "3138. Minimum Length of Anagram Concatenation" -description: "LeetCode 3138. Minimum Length of Anagram Concatenation — using character frequency counting and GCD to determine whether a string is formed by concatenating anagrams. The core technique is enumerating possible segment lengths and verifying consistent character counts across segments. Suitable for algorithm job seekers working through string and math problems." -date: 20/12/2024 -tags: - - Python - - Prefix Sum - - Hash Table - - String - - Counting -abbrlink: d1339d55 -docId: o3knuvbpnki6isfjv3g5ohau -lang: en -translatedFrom: zh -translatedAt: 2026-05-11T00:00:00Z -translatorAgent: claude-sonnet-4-6 ---- - -# Description - -https://leetcode.cn/problems/minimum-length-of-anagram-concatenation/description/?envType=daily-question&envId=2024-12-20 - -You are given a string `s`, which is known to be a concatenation of anagrams of some string `t`. - -Return the minimum possible length of the string `t`. - -An anagram is formed by rearranging the letters of a string. For example, "aab", "aba", and, "baa" are anagrams of "aab". - -#### Example 1: - - Input: s = "abba" - - Output: 2 - - Explanation: - - One possible string t could be "ba". - -#### Example 2: - - Input: s = "cdef" - - Output: 4 - - Explanation: - - One possible string t could be "cdef", notice that t can be equal to s. - -# Thinking - -Since the problem naturally suggests using a counting method (`Counter`), we need to find the minimum substring for each string. For example, for `abba`, the result is `ab`; for `cdef`, it's `cdef`. -We iterate from length `1` (a single character) onwards, slicing the string to get the current substring. - -Initially, we compute the character count for the original string using `Counter`, which gives us a dictionary of character frequencies. -Next, we only need to check if the count of each character in the current substring multiplied by `n/k` equals the count in the original string (i.e., whether repeating the current substring x times equals the original string). - -# Code - -```python -import collections -class Solution: - def minAnagramLength(self, s: str) -> int: - def check(k: int) -> bool: - # 遍历字符串 s,每次取长度为 k 的子串 - # Iterate over the string `s`, taking substrings of length `k` - for i in range(0, n, k): - # 统计每个字符出现的次数 - # Count the occurrences of each character in the current substring - cnt1 = collections.Counter(s[i: i + k]) - for c, v in cnt.items(): - # 如果每个字符出现的次数乘以 n/k != cnt[] return False - # If the count of any character multiplied by (n // k) != the original count, return False - if cnt1[c] * (n // k) != v: - return False - return True - - cnt = collections.Counter(s) - n = len(s) - for i in range(1, n+1): - if n % i == 0 and check(i): - return i -``` diff --git a/content/docs/career/interview-prep/leetcode/3138. Minimum Length of Anagram Concatenation.md b/content/docs/career/interview-prep/leetcode/3138. Minimum Length of Anagram Concatenation.md deleted file mode 100644 index 1fa411d..0000000 --- a/content/docs/career/interview-prep/leetcode/3138. Minimum Length of Anagram Concatenation.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: 3138. Minimum Length of Anagram Concatenation.md -description: "LeetCode 3138. Minimum Length of Anagram Concatenation 题解 — 利用字符频率计数与最大公约数(GCD)判断字符串是否由若干变位词拼接而成,核心技巧是枚举可能的分段长度并验证各段字符计数一致。适合正在刷字符串与数学类题目的算法求职者。" -date: 20/12/2024 -tags: - - Python - - Prefix Sum - - Hash Table - - String - - Counting -abbrlink: d1339d55 -docId: o3knuvbpnki6isfjv3g5ohau ---- - -# Description: - -https://leetcode.cn/problems/minimum-length-of-anagram-concatenation/description/?envType=daily-question&envId=2024-12-20 -You are given a string `s`, which is known to be a concatenation of anagrams of some string `t`. - -Return the minimum possible length of the string `t`. - -An anagram is formed by rearranging the letters of a string. For example, "aab", "aba", and, "baa" are anagrams of "aab". - -#### Example 1: - - Input: s = "abba" - - Output: 2 - - Explanation: - - One possible string t could be "ba". - -#### Example 2: - - Input: s = "cdef" - - Output: 4 - - Explanation: - - One possible string t could be "cdef", notice that t can be equal to s. - -# Thinking: - -Since the problem naturally suggests using a counting method (`Counter`), we need to find the minimum substring for each string. For example, for `abba`, the result is `ab`; for `cdef`, it's `cdef`. -We iterate from length `1` (a single character) onwards, slicing the string to get the current substring. - -Initially, we compute the character count for the original string using `Counter`, which gives us a dictionary of character frequencies. -Next, we only need to check if the count of each character in the current substring multiplied by `n/k` equals the count in the original string (i.e., whether repeating the current substring x times equals the original string). - -由于题意很容易联想到这道题要进行计数(Counter). 我们需要找到每个字符串的最小子串,`abba`为`ab`, `cdef`为`cdef`. 于是我们从长度0(即单个字符)开始遍历,期间通过切片的形式来获取当前子串。 -因为最开始我们对原始字符串进行了Counter,得到了字符数量和字符对应的字典。接下来我们只需要判断当前子串的Counter到的某个字符的数值乘以`n/k`是否等于原始字符串的Counter的值即可(即当前子串乘以x倍是否等于源字符串)。 - -# Code: - -```python -import collections -class Solution: - def minAnagramLength(self, s: str) -> int: - def check(k: int) -> bool: - # 遍历字符串 s,每次取长度为 k 的子串 - # Iterate over the string `s`, taking substrings of length `k` - for i in range(0, n, k): - # 统计每个字符出现的次数 - # Count the occurrences of each character in the current substring - cnt1 = collections.Counter(s[i: i + k]) - for c, v in cnt.items(): - # 如果每个字符出现的次数乘以 n/k != cnt[] return False - # If the count of any character multiplied by (n // k) != the original count, return False - if cnt1[c] * (n // k) != v: - return False - return True - - cnt = collections.Counter(s) - n = len(s) - for i in range(1, n+1): - if n % i == 0 and check(i): - return i -``` diff --git a/content/docs/career/interview-prep/leetcode/42.md b/content/docs/career/interview-prep/leetcode/42-trapping-rain-water.zh.md similarity index 99% rename from content/docs/career/interview-prep/leetcode/42.md rename to content/docs/career/interview-prep/leetcode/42-trapping-rain-water.zh.md index 8e45829..68737df 100644 --- a/content/docs/career/interview-prep/leetcode/42.md +++ b/content/docs/career/interview-prep/leetcode/42-trapping-rain-water.zh.md @@ -1,5 +1,5 @@ --- -title: 42.md +title: "42. 接雨水" description: "LeetCode 42. 接雨水 题解 — 双指针法结合木桶原理,维护左右最高柱子逐格计算可蓄水量。关键技巧:左指针与右指针向中间移动,每次比较当前柱子与对应侧最高值,取短板决定水位高度。适合正在刷 LeetCode 热题、准备算法面试的 CS 求职者。" date: "2025/3/27-19:56" tags: diff --git a/content/docs/career/interview-prep/leetcode/42.en.md b/content/docs/career/interview-prep/leetcode/42.en.md deleted file mode 100644 index f685517..0000000 --- a/content/docs/career/interview-prep/leetcode/42.en.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: "42. Trapping Rain Water" -description: "LeetCode 42. Trapping Rain Water — two-pointer approach combined with the bucket theory: maintain left and right maximum heights to calculate trapped water column by column. Key technique: left and right pointers move inward, comparing each bar's height against the corresponding side's maximum to determine the water level. Suitable for CS job seekers grinding LeetCode hot problems and preparing for algorithm interviews." -date: "2025/3/27-19:56" -tags: - - - Python - - - Answer -abbrlink: 60fe0230 -docId: jv8qj3ljyr2uomaehnv0l77k -lang: en -translatedFrom: zh -translatedAt: 2026-05-11T00:00:00Z -translatorAgent: claude-sonnet-4-6 ---- - -# QUESTION - -[42. Trapping Rain Water]() - -# My Think - -This is a problem I've memorized inside out — totally muscle memory. But I revisited it yesterday and tried to actually understand it. I even made a GIF to help myself visualize what's happening. Here's the code: - -Let's use the example: `[0,1,0,2,1,0,1,3,2,1,2,1]` - -The core idea of this problem is basically the "bucket theory": -We treat the two outermost bars as the "Walls of Maria" — and ignore the inner ones for now. -If that's the case, then the max height of water we can hold is `min(leftmax, rightmax)`. -In other words, the shorter wall decides the water level. - -But height alone isn't enough — we also need width to compute the actual amount of water. -So we look at each bar one by one, and calculate how much water we can trap on top of it, then sum it all up. - -We use two pointers: `left` and `right`, and also keep track of the highest wall on the left (`leftmax`) and the highest wall on the right (`rightmax`). - -When our pointer reaches the end of the string (or the two walls meet), that means we've gone through all the characters — that's when we know we have a valid answer. - -Take this specific frame as an example: - -At this point, the max water we can hold is `leftmax = 2`, but the current column has height `1`, so we can trap: - -`2 - 1 = 1` unit of water. - -If we were comparing `leftmax` and `rightmax` directly, we wouldn't know why this particular column can hold water. The only reason it can trap water is because its height is less than or equal to `leftmax`. - -# Code - -```python -class Solution: - def trap(self, height: list[int]) -> int: - ans = leftmost = rightmost = 0 - left, right = 0, len(height) - 1 - while left < right: - leftmost = max(leftmost, height[left]) - rightmost = max(rightmost, height[right]) - if leftmost <= rightmost: - ans += leftmost - height[left] - left += 1 - else: - ans += rightmost - height[right] - right -= 1 - return ans -``` - -```python -import matplotlib.pyplot as plt -import matplotlib.animation as animation -import numpy as np - -# Given height list for illustration -height = [0,1,0,2,1,0,1,3,2,1,2,1] - -# Initialize variables as in the function -left, right = 0, len(height) - 1 -leftmax = rightmax = 0 -ans = 0 - -# For animation, store each frame's water level and pointers -frames = [] - -# Simulate the logic and capture frames -while left < right: - leftmax = max(leftmax, height[left]) - rightmax = max(rightmax, height[right]) - water = [0] * len(height) - if leftmax <= rightmax: - trapped = max(0, leftmax - height[left]) - ans += trapped - water[left] = trapped - frames.append((height.copy(), water.copy(), left, right)) - left += 1 - else: - trapped = max(0, rightmax - height[right]) - ans += trapped - water[right] = trapped - frames.append((height.copy(), water.copy(), left, right)) - right -= 1 - -# Create animation -fig, ax = plt.subplots(figsize=(10, 5)) - -def update(frame): - ax.clear() - heights, water, l_ptr, r_ptr = frame - indices = np.arange(len(heights)) - ax.bar(indices, heights, color='grey', edgecolor='black') - ax.bar(indices, water, bottom=heights, color='blue', edgecolor='blue', alpha=0.6) - ax.axvline(l_ptr, color='green', linestyle='--', label='Left Pointer') - ax.axvline(r_ptr, color='red', linestyle='--', label='Right Pointer') - ax.set_ylim(0, max(height) + 3) - ax.set_title("Trapping Rain Water Animation") - ax.legend() - -ani = animation.FuncAnimation(fig, update, frames=frames, interval=500, repeat=False) - -from IPython.display import HTML -ani.save("trapping_rain_water.gif", writer="pillow", fps=2) # 保存为 GIF -``` diff --git "a/content/docs/career/interview-prep/leetcode/46.\345\205\250\346\216\222\345\210\227.en.md" "b/content/docs/career/interview-prep/leetcode/46.\345\205\250\346\216\222\345\210\227.en.md" deleted file mode 100644 index de91b96..0000000 --- "a/content/docs/career/interview-prep/leetcode/46.\345\205\250\346\216\222\345\210\227.en.md" +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: "46. Permutations" -description: "LeetCode 46. Permutations — solution using backtracking (DFS) to recursively generate all permutations by swapping array elements for state restoration, avoiding extra space overhead. The key technique is maintaining a swap pointer to fix the current position and recursively processing subsequent positions. Suitable for CS job seekers and algorithm learners preparing for interviews who want to reinforce backtracking skills." -date: 24/3/2025 -tags: - - Python - - "9021" - - tree -abbrlink: d567a4cd -docId: mxt0ux1zpbzph4nuxz51eyg7 -lang: en -translatedFrom: zh -translatedAt: 2026-05-11T00:00:00Z -translatorAgent: claude-sonnet-4-6 ---- - -# Description - -Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. - -Example 1: - -Input: nums = [1,2,3] -Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] -Example 2: - -Input: nums = [0,1] -Output: [[0,1],[1,0]] -Example 3: - -Input: nums = [1] -Output: [[1]] - -# Thinking - -This question is more like a tree problem. -like the tree below: - -```shell -dfs(0): nums = [1,2,3] -| -|-- i=0: swap(0,0) -> [1,2,3] -| | -| |-- dfs(1) -| |-- i=1: swap(1,1) -> [1,2,3] -| | |-- dfs(2): append [1,2,3] -| |-- i=2: swap(1,2) -> [1,3,2] -| |-- dfs(2): append [1,3,2] -| -|-- i=1: swap(0,1) -> [2,1,3] -| | -| |-- dfs(1) -| |-- i=1: swap(1,1) -> [2,1,3] -| | |-- dfs(2): append [2,1,3] -| |-- i=2: swap(1,2) -> [2,3,1] -| |-- dfs(2): append [2,3,1] -| -|-- i=2: swap(0,2) -> [3,2,1] - | - |-- dfs(1) - |-- i=1: swap(1,1) -> [3,2,1] - | |-- dfs(2): append [3,2,1] - |-- i=2: swap(1,2) -> [3,1,2] - |-- dfs(2): append [3,1,2] - -``` - -We swap the current position index with each possible candidate `i` from `index` to the end. They can be seen as left and right pointers: `index` determines which position we're filling, and `i` tries different numbers to place there. - -Before the recursive call, we swap `nums[i]` and `nums[index]` to try placing a new number at position index. If we reach the last position (`index == len(nums) - 1`), we add the current permutation to the answer list. -After recursion, we swap back to restore the original state (backtracking). - -# Code - -```python -class Solution: - def permute(self, nums: List[int]) -> List[List[int]]: - # index - def dfs(index): - # Reach the last element - if index == len(nums) - 1: - res.append(list(nums)) - return - for i in range(index, len(nums)): - nums[i], nums[index] = nums[index], nums[i] - dfs(index + 1) - nums[i], nums[index] = nums[index], nums[i] - - res = [] - dfs(0) - return res -``` diff --git "a/content/docs/career/interview-prep/leetcode/46.\345\205\250\346\216\222\345\210\227.md" "b/content/docs/career/interview-prep/leetcode/46.\345\205\250\346\216\222\345\210\227.md" deleted file mode 100644 index 94103cd..0000000 --- "a/content/docs/career/interview-prep/leetcode/46.\345\205\250\346\216\222\345\210\227.md" +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: 46.permutations -description: "LeetCode 46. 全排列 题解 — 使用回溯法(DFS)递归生成所有排列,通过交换数组元素实现状态回溯,避免额外空间开销。关键技巧是维护一个交换指针来固定当前位置,再递归处理后续位置。适合准备面试、刷题巩固回溯算法的 CS 求职者和算法学习者。" -date: 24/3/2025 -tags: - - Python - - "9021" - - tree -abbrlink: d567a4cd -docId: mxt0ux1zpbzph4nuxz51eyg7 ---- - -# Description: - -Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. - -Example 1: - -Input: nums = [1,2,3] -Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] -Example 2: - -Input: nums = [0,1] -Output: [[0,1],[1,0]] -Example 3: - -Input: nums = [1] -Output: [[1]] - -# Thinking: - -This question is more like a tree problem. -like the tree below: - -```shell -dfs(0): nums = [1,2,3] -| -|-- i=0: swap(0,0) -> [1,2,3] -| | -| |-- dfs(1) -| |-- i=1: swap(1,1) -> [1,2,3] -| | |-- dfs(2): append [1,2,3] -| |-- i=2: swap(1,2) -> [1,3,2] -| |-- dfs(2): append [1,3,2] -| -|-- i=1: swap(0,1) -> [2,1,3] -| | -| |-- dfs(1) -| |-- i=1: swap(1,1) -> [2,1,3] -| | |-- dfs(2): append [2,1,3] -| |-- i=2: swap(1,2) -> [2,3,1] -| |-- dfs(2): append [2,3,1] -| -|-- i=2: swap(0,2) -> [3,2,1] - | - |-- dfs(1) - |-- i=1: swap(1,1) -> [3,2,1] - | |-- dfs(2): append [3,2,1] - |-- i=2: swap(1,2) -> [3,1,2] - |-- dfs(2): append [3,1,2] - -``` - -We swap the current position index with each possible candidate `i` from `index` to the end. They can be seen as left and right pointers: `index` determines which position we're filling, and `i` tries different numbers to place there. - -Before the recursive call, we swap `nums[i]` and `nums[index]` to try placing a new number at position index. If we reach the last position (`index == len(nums) - 1`), we add the current permutation to the answer list. -After recursion, we swap back to restore the original state (backtracking). - -# Code: - -```python -class Solution: - def permute(self, nums: List[int]) -> List[List[int]]: - # index - def dfs(index): - # Reach the last element - if index == len(nums) - 1: - res.append(list(nums)) - return - for i in range(index, len(nums)): - nums[i], nums[index] = nums[index], nums[i] - dfs(index + 1) - nums[i], nums[index] = nums[index], nums[i] - - res = [] - dfs(0) - return res -``` diff --git "a/content/docs/career/interview-prep/leetcode/93\345\244\215\345\216\237Ip\345\234\260\345\235\200.en.md" "b/content/docs/career/interview-prep/leetcode/93\345\244\215\345\216\237Ip\345\234\260\345\235\200.en.md" deleted file mode 100644 index 1eeaa68..0000000 --- "a/content/docs/career/interview-prep/leetcode/93\345\244\215\345\216\237Ip\345\234\260\345\235\200.en.md" +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: "93. Restore IP Addresses" -description: "LeetCode 93. Restore IP Addresses — backtracking algorithm (DFS) in depth: recursively iterates through all possible IP segment combinations, with emphasis on pointer movement, string slicing techniques, and the implementation detail that pass-by-value eliminates manual state rollback. Suitable for CS/AI job seekers preparing for big-tech algorithm interviews who are grinding LeetCode backtracking problems." -date: "2025/3/25-14:03" -tags: - - - Python - - - Answer -abbrlink: 9d0d3b9c -docId: d5evrnoglwjvmyginjq84bl0 -lang: en -translatedFrom: zh -translatedAt: 2026-05-11T00:00:00Z -translatorAgent: claude-sonnet-4-6 ---- - -# QUESTION - -[93. Restore IP Addresses](https://leetcode.cn/problems/restore-ip-addresses/description/?envType=company&envId=mihoyo&favoriteSlug=mihoyo-all) - -# My Think - -This MiHoYo coding test question is very similar to LeetCode 46 (Permutations), and both rely on the backtracking approach. - -As shown in the code, during the first traversal, we may get something like `['2', '5', '5', '2']` as our initial parts, but at this point, we haven't traversed the entire string yet. - -When we enter the next level of DFS, the pointer moves forward by +length, so it effectively moves to the far right of the current segment. -If the pointer has reached the end of the string, it means we've visited all characters — in this case, we've found one valid answer and can add it to the result list. - -One important note: `parts + [part]` is pass-by-value, not by reference like in LeetCode 46. -This means we don't need to manually undo changes (i.e., no need to backtrack with pop()), because each recursive call creates a new list. - -# Code - -```python -from typing import List - -class Solution: - def restoreIpAddresses(self, s: str) -> List[str]: - res = [] - - def backtrack(start: int, parts: List[str]): - # 终止条件:正好4段且用完所有字符 - # Stop condition: exactly 4 segments and all characters used up - if len(parts) == 4: - if start == len(s): - res.append(".".join(parts)) - return - - for length in range(1, 4): # 每段长度1~3 Each segment length 1~3 - if start + length > len(s): - break - part = s[start:start+length] - - # 前导0非法,但0本身合法 - # Leading 0 is illegal, but 0 itself is legal - if len(part) > 1 and part[0] == '0': - continue - - if int(part) <= 255: - backtrack(start + length, parts + [part]) # 注意用 + 避免污染 We need to use + to avoid pollution - - backtrack(0, []) - return res -``` diff --git "a/content/docs/career/interview-prep/leetcode/93\345\244\215\345\216\237Ip\345\234\260\345\235\200.md" "b/content/docs/career/interview-prep/leetcode/93\345\244\215\345\216\237Ip\345\234\260\345\235\200.md" deleted file mode 100644 index 2e42cb4..0000000 --- "a/content/docs/career/interview-prep/leetcode/93\345\244\215\345\216\237Ip\345\234\260\345\235\200.md" +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: 93. Restore IP Addresses -description: "LeetCode 93. Restore IP Addresses 题解 — 回溯算法(DFS)深度解析,通过递归遍历所有可能的IP分段组合,重点讲解指针移动与字符串截断技巧,以及值传递下无需手动撤销操作的实现细节。适合正在刷LeetCode回溯类题目、准备大厂算法面试的CS/AI求职者。" -date: "2025/3/25-14:03" -tags: - - - Python - - - Answer -abbrlink: 9d0d3b9c -docId: d5evrnoglwjvmyginjq84bl0 ---- - -# QUESTION: - -[93. Restore IP Addresses](https://leetcode.cn/problems/restore-ip-addresses/description/?envType=company&envId=mihoyo&favoriteSlug=mihoyo-all) - -# My Think: - -This MiHoYo coding test question is very similar to LeetCode 46 (Permutations), and both rely on the backtracking approach. - -As shown in the code, during the first traversal, we may get something like `['2', '5', '5', '2']` as our initial parts, but at this point, we haven’t traversed the entire string yet. - -When we enter the next level of DFS, the pointer moves forward by +length, so it effectively moves to the far right of the current segment. -If the pointer has reached the end of the string, it means we’ve visited all characters — in this case, we’ve found one valid answer and can add it to the result list. - -One important note: `parts + [part]` is pass-by-value, not by reference like in LeetCode 46. -This means we don’t need to manually undo changes (i.e., no need to backtrack with pop()), because each recursive call creates a new list. - -现在在做的是MiHoyo的笔试题, 这个和46.permutation非常相似, 都是回溯思想. - -如代码所示, 在第一遍遍历中, 我们会得到`['2','5','5','2']` 作为第一个parts, 但是我们并没有遍历完整个字符串. - -这个时候如果我们进入新的dfs的时候, 我们的指针因为刚刚`+length`, 所以我们实际上 -指到的是最右边,所以当当前指针已经指到最右边的时候, 说明我们遍历完了所有字符, 那么这就是答案之一. - -`parts + [part]` 是值传递而不是46的引用传递, 所以不用手动撤销更改 - -# Code: - -```python -from typing import List - -class Solution: - def restoreIpAddresses(self, s: str) -> List[str]: - res = [] - - def backtrack(start: int, parts: List[str]): - # 终止条件:正好4段且用完所有字符 - # Stop condition: exactly 4 segments and all characters used up - if len(parts) == 4: - if start == len(s): - res.append(".".join(parts)) - return - - for length in range(1, 4): # 每段长度1~3 Each segment length 1~3 - if start + length > len(s): - break - part = s[start:start+length] - - # 前导0非法,但0本身合法 - # Leading 0 is illegal, but 0 itself is legal - if len(part) > 1 and part[0] == '0': - continue - - if int(part) <= 255: - backtrack(start + length, parts + [part]) # 注意用 + 避免污染 We need to use + to avoid pollution - - backtrack(0, []) - return res -``` diff --git "a/content/docs/career/interview-prep/leetcode/[1545]\346\211\276\345\207\272\347\254\254 N \344\270\252\344\272\214\350\277\233\345\210\266\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254 K \344\275\215.en.md" "b/content/docs/career/interview-prep/leetcode/[1545]\346\211\276\345\207\272\347\254\254 N \344\270\252\344\272\214\350\277\233\345\210\266\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254 K \344\275\215.en.md" deleted file mode 100644 index 02ec4bd..0000000 --- "a/content/docs/career/interview-prep/leetcode/[1545]\346\211\276\345\207\272\347\254\254 N \344\270\252\344\272\214\350\277\233\345\210\266\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254 K \344\275\215.en.md" +++ /dev/null @@ -1,146 +0,0 @@ ---- -title: "1545. Find Kth Bit in Nth Binary String" -description: "LeetCode 1545 solution — Find Kth Bit in Nth Binary String. Uses recursion and mathematical bit-flipping: by analyzing the string construction pattern (S₁=0, each subsequent string is the previous string + 1 + reverse-invert of the previous string), the k-th bit is classified into left half, middle, or right half and solved recursively, avoiding the memory overflow caused by brute-force generation. Ideal for algorithm learners preparing for interviews and practicing recursion and bit manipulation." -date: "2026.03.04 00:46" -tags: - - Leetcode - - answer - - Math - - String -docId: zuoplhoodv7tzfgku0pwzi6w -lang: en -translatedFrom: zh -translatedAt: 2026-05-11T00:00:00Z -translatorAgent: claude-sonnet-4-6 ---- - -# Problem - -Given two positive integers `n` and `k`, the binary string `Sn` is formed according to the following rules: - -- `S1 = "0"` -- When `i > 1`: `Si = Si-1 + "1" + reverse(invert(Si-1))` - -where `+` denotes concatenation, `reverse(x)` returns the string `x` reversed, and `invert(x)` flips every bit in `x` (0 becomes 1 and 1 becomes 0). - -The first 4 strings in this sequence are: - -- S1 = "0" -- S2 = "011" -- S3 = "0111001" -- S4 = "011100110110001" - -Return the `k`-th bit in `Sn`. It is guaranteed that `k` is within the valid range for `Sn`. - -# Solution - -My first attempt was a brute-force approach — directly generating `Sn`. However, when `n` is large, `Sn` becomes extremely long, causing memory overflow. - -```javascript -/** - * @param {number} n - * @param {number} k - * @return {character} - */ -var findKthBit = function (n, k) { - var reverseR = function (input) { - return input - .split("") // 拆分成数组 ["0", "1", "1", "1", "0"] - .map((char) => char ^ 1) // 翻转每一位: [1, 0, 0, 0, 1] - .reverse() // 反转数组顺序: [1, 0, 0, 0, 1] - .join(""); // 拼回字符串 "10001" - }; - let S = "0"; - for (let i = 1; i < n; i++) { - S = S + "1" + reverseR(S); - } - return S[k - 1]; -}; -``` - -I then tried a mathematical bit-flip approach. Observing $S_i = S_{i-1} + "1" + \text{reverse}(\text{invert}(S_{i-1}))$: - -Length pattern: $|S_n| = 2^n - 1$. - -- For example, $S_1$ has length $2^1-1=1$, and its middle bit is position $1$. -- $S_2$ has length $2^2-1=3$, and its middle bit is position $2$. -- $S_3$ has length $2^3-1=7$, and its middle bit is position $4$. - -Three cases for the position of `k`: - -- **Left half** ($k < mid$): This is an exact copy of $S_{n-1}$, so we simply ask "what is the $k$-th bit of $S_{n-1}$?" -- **Middle** ($k = mid$): By the construction rule, this bit is always `"1"`. -- **Right half** ($k > mid$): This is the most elegant part. The right half is the reverse-invert of $S_{n-1}$. - -Because of the **reverse**, the 1st character of the right half corresponds to the last character of the left half, and so on. - -The mapping formula is: $S_n[k] = \text{invert}(S_{n-1}[2^n - k])$. - -For example, to find the 6th bit in $S_3$ (length 7), it corresponds to the result of inverting the $2^3 - 6 = 2$nd bit of $S_2$. - -```javascript -var findKthBit = function (n, k) { - let flip = false; // 记录需要取反的次数 - while (n > 1) { - let mid = 1 << (n - 1); // 2^(n-1) - if (k === mid) { - // 中间位固定为 1 - let res = 1; - return (flip ? res ^ 1 : res).toString(); - } else if (k > mid) { - // 如果在右侧,镜像到左侧,并增加一次取反 - k = 2 * mid - k; - flip = !flip; - } - // 如果在左侧,直接继续看 n-1 - n--; - } - // 最终回到 S1,S1 是 "0" - let res = 0; - return (flip ? res ^ 1 : res).toString(); -}; -``` - -## Why Is `mid` Equal to $2^{n-1}$? - -We can derive the center point (mid) by computing the total length of $S_n$. - -**Computing the length of $S_n$**: Let $L_n$ denote the length of the $n$-th string $S_n$. From the problem rules: - -- $S_1 = "0"$, so $L_1 = 1$ -- $S_n = S_{n-1} + "1" + \text{modified } S_{n-1}$ - -The length recurrence is: $$L_n = L_{n-1} (\text{left half}) + 1 (\text{middle}) + L_{n-1} (\text{right half})$$ i.e., $L_n = 2 \times L_{n-1} + 1$ - -Listing out the values: - -- $L_1 = 1$ -- $L_2 = 2 \times 1 + 1 = 3$ -- $L_3 = 2 \times 3 + 1 = 7$ -- $L_4 = 2 \times 7 + 1 = 15$ - -Pattern: $L_n = 2^n - 1$ - -## 2. Why Do We Search This Way? - -Think of it like finding a specific point on an infinitely folded paper strip: - -- **Old approach (simulation)**: Fold a blank sheet of paper 20 times to create an enormously long strip, then count from the beginning to the $k$-th point. -- **New approach (backtracking/iteration)**: Look at the already "folded" paper ($S_n$) and ask: is this point $k$ to the left or right of the fold? - - If it is to the right of the fold, "unfold" it back to the left (mirror mapping) and record that it was flipped once (`flip = !flip`). - - If it is to the left, just look directly at the left half. - - Now the paper is half the size (`n--`), and you repeat the process until you land exactly on the fold (`k === mid`) or the paper shrinks to its smallest possible size (`n=1`). - -## 3. If We Are in the Right Half and `S[k]=0`, Does Flipping Over Mean `S[k]=1`? - -Why does "flipping over" give us 1? - -According to the problem, the right half of $S_i$ is: $\text{reverse}(\text{invert}(S_{i-1}))$. There are two operations here: - -- **Invert**: $0 \to 1$, $1 \to 0$. -- **Reverse**: positions are mirrored left-to-right. - -So if we see a 0 in the right half and trace it back through these two operations: - -- Because of **invert**: it was a 1 before being flipped. -- Because of **reverse**: its corresponding position is the symmetric point in the left half. diff --git a/content/docs/learn/ai/ai-math-basics/linear-algebra/resources/index.en.mdx b/content/docs/learn/ai/ai-math-basics/linear-algebra/resources/index.en.mdx index b00e664..fe6987b 100644 --- a/content/docs/learn/ai/ai-math-basics/linear-algebra/resources/index.en.mdx +++ b/content/docs/learn/ai/ai-math-basics/linear-algebra/resources/index.en.mdx @@ -1,5 +1,5 @@ --- -title: References +title: Linear Algebra References description: "Explore curated references for linear algebra and calculus, including immersive texts, geometric PDFs, and 3Blue1Brown’s visual video series—ideal for CS/AI learners building intuition." date: "2024-01-10" tags: diff --git a/content/docs/learn/ai/ai-math-basics/linear-algebra/resources/index.mdx b/content/docs/learn/ai/ai-math-basics/linear-algebra/resources/index.mdx index f43fe8e..54ad0fd 100644 --- a/content/docs/learn/ai/ai-math-basics/linear-algebra/resources/index.mdx +++ b/content/docs/learn/ai/ai-math-basics/linear-algebra/resources/index.mdx @@ -1,5 +1,5 @@ --- -title: 参考资料 +title: 线性代数参考资料 description: "内卷地狱技术文档页汇总线性代数核心学习资源:沉浸式线性代数互动教程、3Blue1Brown《线性代数的本质》系列视频与《线性代数的几何意义》PDF,侧重几何直观与可视化理解。适合CS/AI求职者、考研学生及希望夯实数学基础的开发者。" date: "2024-01-10" tags: diff --git a/content/docs/learn/ai/ai-math-basics/probability-statistics/resources/index.en.mdx b/content/docs/learn/ai/ai-math-basics/probability-statistics/resources/index.en.mdx index f3e64d0..85ebe37 100644 --- a/content/docs/learn/ai/ai-math-basics/probability-statistics/resources/index.en.mdx +++ b/content/docs/learn/ai/ai-math-basics/probability-statistics/resources/index.en.mdx @@ -1,5 +1,5 @@ --- -title: References +title: Probability & Statistics References description: "Explore curated references on involutionhell.com: key CS/AI papers, career resources, and open-source learning materials for developers and researchers." date: "2024-01-11" tags: diff --git a/content/docs/learn/ai/ai-math-basics/probability-statistics/resources/index.mdx b/content/docs/learn/ai/ai-math-basics/probability-statistics/resources/index.mdx index 5169d1e..48a4921 100644 --- a/content/docs/learn/ai/ai-math-basics/probability-statistics/resources/index.mdx +++ b/content/docs/learn/ai/ai-math-basics/probability-statistics/resources/index.mdx @@ -1,5 +1,5 @@ --- -title: 参考资料 +title: 概率论与统计学参考资料 description: "内卷地狱 involutionhell.com 技术文档页汇总计算机科学、人工智能与求职面试的核心参考资料,涵盖算法、系统设计及机器学习面试高频题解与开源学习路径,适合正在备战大厂校招、社招或系统提升 CS/AI 基础能力的开发者与求职者阅读。" date: "2024-01-11" tags: diff --git a/source.config.ts b/source.config.ts index a7dffd4..5cae375 100644 --- a/source.config.ts +++ b/source.config.ts @@ -36,6 +36,41 @@ function remarkNormalizeCodeLang() { }; } +/** + * Remark 插件:避免双 h1 + * + * 背景:Bing Webmaster 2026-05 报告 4 个页面有多个

,仓库里其实 + * 181 / 324 个 MDX 正文都写了 `# 一级标题`。page.tsx 已经渲染了 + * `

{frontmatter.title}

` 作为页面唯一 h1,MDX 正文里再写 `# x` + * 就形成双 h1,对 SEO/无障碍都是反模式。 + * + * 不能要求贡献者注意这个约束(markdown 习惯就是 # 当顶级标题),所以 + * 在 build 阶段自动 shift:如果 MDX 含 h1,整树 heading 降一级 + * (h1→h2 / h2→h3 / ... / h5→h6)。这样: + * - 贡献者照常写 `# 标题`、`## 章节`、`### 子节` + * - 渲染出来变 `

` / `

` / `

`,保持层级关系 + * - page.tsx 的 `

` 是页面唯一 h1 + * + * 如果 MDX 不含 h1(作者已经从 ## 开始)就不动 —— 这种文档天然合规。 + * + * 副作用:MDX 用到 h5 时会被降到 h6;h6 已是 markdown 最大深度无法再降, + * 这里保留为 h6(罕见,CS/AI 技术文档基本不到 5 层)。 + */ +function remarkShiftHeadingIfH1() { + return (tree: Root) => { + // 用 visit 遍历整树检测 h1:h1 可能嵌套在 blockquote / list 里 + // (markdown 语法允许 `> # title`),只看 tree.children 顶级会漏掉 + let hasH1 = false; + visit(tree, "heading", (node) => { + if (node.depth === 1) hasH1 = true; + }); + if (!hasH1) return; + visit(tree, "heading", (node) => { + if (node.depth < 6) node.depth += 1; + }); + }; +} + /** * CRITICAL: Fumadocs 图片处理配置 * @@ -86,13 +121,18 @@ const imageOptions = { * 包含: * - remarkMath:启用 Markdown 数学语法支持 ($...$, $$...$$) * - remarkNormalizeCodeLang:将代码块语言标识符转为小写(解决 Shiki 大小写问题) + * - remarkShiftHeadingIfH1:mdx 含 h1 时整树 heading 降一级,避免与 page.tsx 的 h1 冲突 * - rehypeKatex:使用 KaTeX 将数学公式渲染为 HTML(strict:false 更宽松) * - remarkImageOptions:图片处理配置(禁用远程尺寸探测,见上方注释) */ export default defineConfig({ mdxOptions: { - // 支持 LaTeX 公式 + 规范化代码块语言标识符 - remarkPlugins: [remarkMath, remarkNormalizeCodeLang], + // 支持 LaTeX 公式 + 规范化代码块 + h1 降级避免双 h1 + remarkPlugins: [ + remarkMath, + remarkNormalizeCodeLang, + remarkShiftHeadingIfH1, + ], // 宽松的 KaTeX 渲染,不因轻微语法错误中断 rehypePlugins: (v) => [[rehypeKatex, { strict: false }], ...v],