diff --git a/README.md b/README.md index d8fc9ae3908c8..40168a949f73b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ## 介绍 -本项目包含 LeetCode、《剑指 Offer(第 2 版)》、《剑指 Offer(专项突击版)》、《程序员面试金典(第 6 版)》等题目的相关题解。所有题解均由多种编程语言实现,包括但不限于:Java、Python、C++、Go、TypeScript、Rust,日常更新。欢迎 Star 🌟 关注[本项目](https://github.com/doocs/leetcode),获取项目最新动态。 +本项目包含 LeetCode、《剑指 Offer(第 2 版)》、《剑指 Offer(专项突击版)》、《程序员面试金典(第 6 版)》等题目的相关题解。所有题解均由多种编程语言实现,包括但不限于:Java、Python、C++、Go、TypeScript、Rust。我们正在全力更新,欢迎 Star 🌟 关注[本项目](https://github.com/doocs/leetcode),获取项目最新动态。 [English Version](/README_EN.md) diff --git a/README_EN.md b/README_EN.md index 72060d68c0d63..12f7ad22a0a39 100644 --- a/README_EN.md +++ b/README_EN.md @@ -14,9 +14,7 @@ ## Introduction -The Doocs LeetCode repository is a comprehensive collection of solutions to LeetCode questions in multiple programming languages. The repository contains solutions to LeetCode, LCOF, LCCI questions, and more in multiple programming languages. - -The repository is maintained by the Doocs community, and please give us a [star](https://github.com/doocs/leetcode) 🌟 if you like it. +This project contains solutions for problems from LeetCode, "Coding Interviews (2nd Edition)", "Coding Interviews (Special Edition)", "Cracking the Coding Interview (6th Edition)", etc. All solutions are implemented in multiple programming languages, including but not limited to: Java, Python, C++, Go, TypeScript, Rust. We are working hard to update, welcome to Star 🌟 and follow [this project](https://github.com/doocs/leetcode) to get the latest updates. [中文文档](/README.md) diff --git a/solution/2400-2499/2485.Find the Pivot Integer/README_EN.md b/solution/2400-2499/2485.Find the Pivot Integer/README_EN.md index ffc2625458cf4..9eca90b7888ae 100644 --- a/solution/2400-2499/2485.Find the Pivot Integer/README_EN.md +++ b/solution/2400-2499/2485.Find the Pivot Integer/README_EN.md @@ -46,6 +46,34 @@ ## Solutions +**Solution 1: Enumeration** + +We can directly enumerate $x$ in the range of $[1,..n]$, and check whether the following equation holds. If it holds, then $x$ is the pivot integer, and we can directly return $x$. + +$$ +(1 + x) \times x = (x + n) \times (n - x + 1) +$$ + +The time complexity is $O(n)$, where $n$ is the given positive integer $n$. The space complexity is $O(1)$. + +**Solution 2: Mathematics** + +We can transform the above equation to get: + +$$ +n \times (n + 1) = 2 \times x^2 +$$ + +That is: + +$$ +x = \sqrt{\frac{n \times (n + 1)}{2}} +$$ + +If $x$ is an integer, then $x$ is the pivot integer, otherwise there is no pivot integer. + +The time complexity is $O(1)$, and the space complexity is $O(1)$. + ### **Python3** diff --git a/solution/2400-2499/2486.Append Characters to String to Make Subsequence/README.md b/solution/2400-2499/2486.Append Characters to String to Make Subsequence/README.md index 9ddda45d7a0f5..3b81f1184c580 100644 --- a/solution/2400-2499/2486.Append Characters to String to Make Subsequence/README.md +++ b/solution/2400-2499/2486.Append Characters to String to Make Subsequence/README.md @@ -57,7 +57,7 @@ **方法一:双指针** -定义两个指针 $i$ 和 $j$,分别指向字符串 $s$ 和 $t$ 的首字符。遍历字符串 $t$,当 $s[i] \neq t[j]$ 时,指针 $i$ 后移,直到 $s[i] = t[j]$ 或者 $i$ 到达字符串 $s$ 的末尾。如果 $i$ 到达字符串 $s$ 的末尾,说明 $t$ 中的字符 $t[j]$ 无法在 $s$ 中找到对应的字符,返回 $t$ 中剩余的字符数。否则,将指针 $i$ 和 $j$ 同时后移,继续遍历字符串 $t$。 +我们定义两个指针 $i$ 和 $j$,分别指向字符串 $s$ 和 $t$ 的首字符。遍历字符串 $t$,当 $s[i] \neq t[j]$ 时,指针 $i$ 后移,直到 $s[i] = t[j]$ 或者 $i$ 到达字符串 $s$ 的末尾。如果 $i$ 到达字符串 $s$ 的末尾,说明 $t$ 中的字符 $t[j]$ 无法在 $s$ 中找到对应的字符,返回 $t$ 中剩余的字符数。否则,将指针 $i$ 和 $j$ 同时后移,继续遍历字符串 $t$。 时间复杂度 $(m + n)$,空间复杂度 $O(1)$。其中 $m$ 和 $n$ 分别是字符串 $s$ 和 $t$ 的长度。 @@ -70,13 +70,12 @@ ```python class Solution: def appendCharacters(self, s: str, t: str) -> int: - m, n = len(s), len(t) - i = 0 - for j in range(n): - while i < m and s[i] != t[j]: + i, m = 0, len(s) + for j, c in enumerate(t): + while i < m and s[i] != c: i += 1 if i == m: - return n - j + return len(t) - j i += 1 return 0 ``` @@ -139,6 +138,24 @@ func appendCharacters(s string, t string) int { } ``` +### **TypeScript** + +```ts +function appendCharacters(s: string, t: string): number { + const [m, n] = [s.length, t.length]; + for (let i = 0, j = 0; j < n; ++j) { + while (i < m && s[i] !== t[j]) { + ++i; + } + if (i === m) { + return n - j; + } + ++i; + } + return 0; +} +``` + ### **...** ``` diff --git a/solution/2400-2499/2486.Append Characters to String to Make Subsequence/README_EN.md b/solution/2400-2499/2486.Append Characters to String to Make Subsequence/README_EN.md index c5779c76fcbf2..58920f972b2e2 100644 --- a/solution/2400-2499/2486.Append Characters to String to Make Subsequence/README_EN.md +++ b/solution/2400-2499/2486.Append Characters to String to Make Subsequence/README_EN.md @@ -49,6 +49,12 @@ It can be shown that appending any 4 characters to the end of s will never make ## Solutions +**Solution 1: Two Pointers** + +We define two pointers $i$ and $j$, pointing to the first characters of strings $s$ and $t$ respectively. We traverse string $t$, when $s[i] \neq t[j]$, we move pointer $i$ forward until $s[i] = t[j]$ or $i$ reaches the end of string $s$. If $i$ reaches the end of string $s$, it means that the character $t[j]$ in $t$ cannot find the corresponding character in $s$, so we return the remaining number of characters in $t$. Otherwise, we move both pointers $i$ and $j$ forward and continue to traverse string $t$. + +The time complexity is $O(m + n)$, and the space complexity is $O(1)$. Where $m$ and $n$ are the lengths of strings $s$ and $t$ respectively. + ### **Python3** @@ -56,13 +62,12 @@ It can be shown that appending any 4 characters to the end of s will never make ```python class Solution: def appendCharacters(self, s: str, t: str) -> int: - m, n = len(s), len(t) - i = 0 - for j in range(n): - while i < m and s[i] != t[j]: + i, m = 0, len(s) + for j, c in enumerate(t): + while i < m and s[i] != c: i += 1 if i == m: - return n - j + return len(t) - j i += 1 return 0 ``` @@ -123,6 +128,24 @@ func appendCharacters(s string, t string) int { } ``` +### **TypeScript** + +```ts +function appendCharacters(s: string, t: string): number { + const [m, n] = [s.length, t.length]; + for (let i = 0, j = 0; j < n; ++j) { + while (i < m && s[i] !== t[j]) { + ++i; + } + if (i === m) { + return n - j; + } + ++i; + } + return 0; +} +``` + ### **...** ``` diff --git a/solution/2400-2499/2486.Append Characters to String to Make Subsequence/Solution.py b/solution/2400-2499/2486.Append Characters to String to Make Subsequence/Solution.py index 1cebb82558e7f..d23af9b595e97 100644 --- a/solution/2400-2499/2486.Append Characters to String to Make Subsequence/Solution.py +++ b/solution/2400-2499/2486.Append Characters to String to Make Subsequence/Solution.py @@ -1,11 +1,10 @@ -class Solution: - def appendCharacters(self, s: str, t: str) -> int: - m, n = len(s), len(t) - i = 0 - for j in range(n): - while i < m and s[i] != t[j]: - i += 1 - if i == m: - return n - j - i += 1 - return 0 +class Solution: + def appendCharacters(self, s: str, t: str) -> int: + i, m = 0, len(s) + for j, c in enumerate(t): + while i < m and s[i] != c: + i += 1 + if i == m: + return len(t) - j + i += 1 + return 0 diff --git a/solution/2400-2499/2486.Append Characters to String to Make Subsequence/Solution.ts b/solution/2400-2499/2486.Append Characters to String to Make Subsequence/Solution.ts new file mode 100644 index 0000000000000..349b20c8889bd --- /dev/null +++ b/solution/2400-2499/2486.Append Characters to String to Make Subsequence/Solution.ts @@ -0,0 +1,13 @@ +function appendCharacters(s: string, t: string): number { + const [m, n] = [s.length, t.length]; + for (let i = 0, j = 0; j < n; ++j) { + while (i < m && s[i] !== t[j]) { + ++i; + } + if (i === m) { + return n - j; + } + ++i; + } + return 0; +} diff --git a/solution/2400-2499/2487.Remove Nodes From Linked List/README.md b/solution/2400-2499/2487.Remove Nodes From Linked List/README.md index 17d8d57f4a7a5..16a68fe38c876 100644 --- a/solution/2400-2499/2487.Remove Nodes From Linked List/README.md +++ b/solution/2400-2499/2487.Remove Nodes From Linked List/README.md @@ -56,6 +56,12 @@ 时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是链表的长度。 +我们也可以不使用数组 $nums$,直接遍历链表,维护一个从栈底到栈顶单调递减的栈 $stk$,如果当前元素比栈顶元素大,则将栈顶元素出栈,直到当前元素小于等于栈顶元素。然后,如果栈不为空,则将栈顶元素的 $next$ 指针指向当前元素,否则将答案链表的虚拟头节点的 $next$ 指针指向当前元素。最后,将当前元素入栈,继续遍历链表。 + +遍历结束后,将虚拟头节点的 $next$ 指针作为答案返回。 + +时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是链表的长度。 + ### **Python3** @@ -87,6 +93,29 @@ class Solution: return dummy.next ``` +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode(next=head) + cur = head + stk = [] + while cur: + while stk and stk[-1].val < cur.val: + stk.pop() + if stk: + stk[-1].next = cur + else: + dummy.next = cur + stk.append(cur) + cur = cur.next + return dummy.next +``` + ### **Java** @@ -127,6 +156,37 @@ class Solution { } ``` +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + public ListNode removeNodes(ListNode head) { + ListNode dummy = new ListNode(0, head); + Deque stk = new ArrayDeque<>(); + for (ListNode cur = head; cur != null; cur = cur.next) { + while (!stk.isEmpty() && stk.peekLast().val < cur.val) { + stk.pollLast(); + } + if (!stk.isEmpty()) { + stk.peekLast().next = cur; + } else { + dummy.next = cur; + } + stk.offerLast(cur); + } + return dummy.next; + } +} +``` + ### **C++** ```cpp @@ -166,6 +226,39 @@ public: }; ``` +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeNodes(ListNode* head) { + ListNode* dummy = new ListNode(0, head); + ListNode* cur = head; + vector stk; + for (ListNode* cur = head; cur; cur = cur->next) { + while (stk.size() && stk.back()->val < cur->val) { + stk.pop_back(); + } + if (stk.size()) { + stk.back()->next = cur; + } else { + dummy->next = cur; + } + stk.push_back(cur); + } + return dummy->next; + } +}; +``` + ### **Go** ```go @@ -199,6 +292,32 @@ func removeNodes(head *ListNode) *ListNode { } ``` +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func removeNodes(head *ListNode) *ListNode { + dummy := &ListNode{Next: head} + stk := []*ListNode{} + for cur := head; cur != nil; cur = cur.Next { + for len(stk) > 0 && stk[len(stk)-1].Val < cur.Val { + stk = stk[:len(stk)-1] + } + if len(stk) > 0 { + stk[len(stk)-1].Next = cur + } else { + dummy.Next = cur + } + stk = append(stk, cur) + } + return dummy.Next +} +``` + ### **TypeScript** ```ts @@ -236,6 +355,37 @@ function removeNodes(head: ListNode | null): ListNode | null { } ``` +```ts +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function removeNodes(head: ListNode | null): ListNode | null { + const dummy = new ListNode(0, head); + const stk: ListNode[] = []; + for (let cur = head; cur; cur = cur.next) { + while (stk.length && stk.at(-1)!.val < cur.val) { + stk.pop(); + } + if (stk.length) { + stk.at(-1)!.next = cur; + } else { + dummy.next = cur; + } + stk.push(cur); + } + return dummy.next; +} +``` + ### **...** ``` diff --git a/solution/2400-2499/2487.Remove Nodes From Linked List/README_EN.md b/solution/2400-2499/2487.Remove Nodes From Linked List/README_EN.md index 9a50373f14f20..1f27507a91ba8 100644 --- a/solution/2400-2499/2487.Remove Nodes From Linked List/README_EN.md +++ b/solution/2400-2499/2487.Remove Nodes From Linked List/README_EN.md @@ -48,6 +48,12 @@ Finally, we construct the resulting linked list from the bottom to the top of th The time complexity is $O(n)$, and the space complexity is $O(n)$, where $n$ is the length of the linked list. +We can also directly traverse the linked list without using the array $nums$, maintaining a stack $stk$ that is monotonically decreasing from the bottom to the top. If the current element is larger than the top element of the stack, we pop the top element of the stack until the current element is less than or equal to the top element. Then, if the stack is not empty, we set the $next$ pointer of the top element of the stack to the current element. Otherwise, we set the $next$ pointer of the dummy head node of the answer linked list to the current element. Finally, we push the current element into the stack and continue to traverse the linked list. + +After the traversal, we return the $next$ pointer of the dummy head node as the answer. + +The time complexity is $O(n)$, and the space complexity is $O(n)$, where $n$ is the length of the linked list. + ### **Python3** @@ -77,6 +83,29 @@ class Solution: return dummy.next ``` +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode(next=head) + cur = head + stk = [] + while cur: + while stk and stk[-1].val < cur.val: + stk.pop() + if stk: + stk[-1].next = cur + else: + dummy.next = cur + stk.append(cur) + cur = cur.next + return dummy.next +``` + ### **Java** ```java @@ -115,6 +144,37 @@ class Solution { } ``` +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + public ListNode removeNodes(ListNode head) { + ListNode dummy = new ListNode(0, head); + Deque stk = new ArrayDeque<>(); + for (ListNode cur = head; cur != null; cur = cur.next) { + while (!stk.isEmpty() && stk.peekLast().val < cur.val) { + stk.pollLast(); + } + if (!stk.isEmpty()) { + stk.peekLast().next = cur; + } else { + dummy.next = cur; + } + stk.offerLast(cur); + } + return dummy.next; + } +} +``` + ### **C++** ```cpp @@ -154,6 +214,39 @@ public: }; ``` +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeNodes(ListNode* head) { + ListNode* dummy = new ListNode(0, head); + ListNode* cur = head; + vector stk; + for (ListNode* cur = head; cur; cur = cur->next) { + while (stk.size() && stk.back()->val < cur->val) { + stk.pop_back(); + } + if (stk.size()) { + stk.back()->next = cur; + } else { + dummy->next = cur; + } + stk.push_back(cur); + } + return dummy->next; + } +}; +``` + ### **Go** ```go @@ -187,6 +280,32 @@ func removeNodes(head *ListNode) *ListNode { } ``` +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func removeNodes(head *ListNode) *ListNode { + dummy := &ListNode{Next: head} + stk := []*ListNode{} + for cur := head; cur != nil; cur = cur.Next { + for len(stk) > 0 && stk[len(stk)-1].Val < cur.Val { + stk = stk[:len(stk)-1] + } + if len(stk) > 0 { + stk[len(stk)-1].Next = cur + } else { + dummy.Next = cur + } + stk = append(stk, cur) + } + return dummy.Next +} +``` + ### **TypeScript** ```ts @@ -224,6 +343,37 @@ function removeNodes(head: ListNode | null): ListNode | null { } ``` +```ts +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function removeNodes(head: ListNode | null): ListNode | null { + const dummy = new ListNode(0, head); + const stk: ListNode[] = []; + for (let cur = head; cur; cur = cur.next) { + while (stk.length && stk.at(-1)!.val < cur.val) { + stk.pop(); + } + if (stk.length) { + stk.at(-1)!.next = cur; + } else { + dummy.next = cur; + } + stk.push(cur); + } + return dummy.next; +} +``` + ### **...** ``` diff --git a/solution/2900-2999/2983.Palindrome Rearrangement Queries/README.md b/solution/2900-2999/2983.Palindrome Rearrangement Queries/README.md index 49329e44d0da1..c2e2b8c43f662 100644 --- a/solution/2900-2999/2983.Palindrome Rearrangement Queries/README.md +++ b/solution/2900-2999/2983.Palindrome Rearrangement Queries/README.md @@ -91,6 +91,27 @@ a0 = 1, b0 = 2, c0 = 4, d0 = 5. +**方法一:前缀和 + 分类讨论** + +我们记字符串 $s$ 的长度为 $n$,那么一半的长度为 $m = \frac{n}{2}$。接下来,我们把字符串 $s$ 分成长度相等的两段,其中第二段反转后得到字符串 $t$,第一段记为 $s$。那么对于每个查询 $[a_i, b_i, c_i, d_i]$,其中 $c_i$ 和 $d_i$ 需要变换为 $n - 1 - d_i$ 和 $n - 1 - c_i$。问题转化为:对于每个查询 $[a_i, b_i, c_i, d_i]$,判断 $s[a_i, b_i]$ 和 $t[c_i, d_i]$ 是否可以通过重新排列,使得字符串 $s$ 和 $t$ 相等。 + +我们预处理以下信息: + +1. 字符串 $s$ 的前缀和数组 $pre_1$,其中 $pre_1[i][j]$ 表示字符串 $s$ 前 $i$ 个字符中字符 $j$ 的数量; +2. 字符串 $t$ 的前缀和数组 $pre_2$,其中 $pre_2[i][j]$ 表示字符串 $t$ 前 $i$ 个字符中字符 $j$ 的数量; +3. 字符串 $s$ 和 $t$ 的差异数组 $diff$,其中 $diff[i]$ 表示字符串 $s$ 和 $t$ 的前 $i$ 个字符中不同的字符数量。 + +对于每个查询 $[a_i, b_i, c_i, d_i]$,我们不妨假设 $a_i \le c_i$,那么我们需要讨论以下几种情况: + +1. 字符串 $s$ 和 $t$ 的前缀子串 $s[..a_i-1]$ 和 $t[..a_i-1]$ 必须相等,并且后缀子串 $s[\max(b_i, d_i)+1..]$ 和 $t[\max(b_i, d_i)..]$ 也必须相等,否则无法通过重新排列使得字符串 $s$ 和 $t$ 相等; +1. 如果 $d_i \le b_i$,说明区间 $[a_i, b_i]$ 包含区间 $[c_i, d_i]$,那么如果 $s[a_i, b_i]$ 和 $t[a_i, b_i]$ 这两个子串包含的字符数量相同,那么就可以通过重新排列使得字符串 $s$ 和 $t$ 相等,否则无法通过重新排列使得字符串 $s$ 和 $t$ 相等; +1. 如果 $b_i < c_i$,说明区间 $[a_i, b_i]$ 和区间 $[c_i, d_i]$ 不相交,那么 $s[b_i+1, c_i-1]$ 和 $t[b_i+1, c_i-1]$ 这两个子串必须相等,并且 $s[a_i, b_i]$ 和 $t[a_i, b_i]$ 这两个子串必须相等,同时 $s[c_i, d_i]$ 和 $t[c_i, d_i]$ 这两个子串必须相等,否则无法通过重新排列使得字符串 $s$ 和 $t$ 相等。 +1. 如果 $c_i \le b_i < d_i$,说明区间 $[a_i, b_i]$ 和区间 $[c_i, d_i]$ 相交,那么 $s[a_i, b_i]$ 包含的字符,减去 $t[a_i, c_i-1]$ 包含的字符,必须等于 $t[c_i, d_i]$ 包含的字符,减去 $s[b_i+1, d_i]$ 包含的字符,否则无法通过重新排列使得字符串 $s$ 和 $t$ 相等。 + +基于上述分析,我们遍历每个查询 $[a_i, b_i, c_i, d_i]$,判断是否满足上述条件即可。 + +时间复杂度 $O((n + q) \times |\Sigma|)$,空间复杂度 $O(n \times |\Sigma|)$。其中 $n$ 和 $q$ 分别是字符串 $s$ 的长度和查询数组 $queries$ 的长度;而 $|\Sigma|$ 是字符集的大小,本题中字符集为小写英文字母,因此 $|\Sigma| = 26$。 + ### **Python3** @@ -98,7 +119,59 @@ a0 = 1, b0 = 2, c0 = 4, d0 = 5. ```python - +class Solution: + def canMakePalindromeQueries(self, s: str, queries: List[List[int]]) -> List[bool]: + def count(pre: List[List[int]], i: int, j: int) -> List[int]: + return [x - y for x, y in zip(pre[j + 1], pre[i])] + + def sub(cnt1: List[int], cnt2: List[int]) -> List[int]: + res = [] + for x, y in zip(cnt1, cnt2): + if x - y < 0: + return [] + res.append(x - y) + return res + + def check( + pre1: List[List[int]], pre2: List[List[int]], a: int, b: int, c: int, d: int + ) -> bool: + if diff[a] > 0 or diff[m] - diff[max(b, d) + 1] > 0: + return False + if d <= b: + return count(pre1, a, b) == count(pre2, a, b) + if b < c: + return ( + diff[c] - diff[b + 1] == 0 + and count(pre1, a, b) == count(pre2, a, b) + and count(pre1, c, d) == count(pre2, c, d) + ) + cnt1 = sub(count(pre1, a, b), count(pre2, a, c - 1)) + cnt2 = sub(count(pre2, c, d), count(pre1, b + 1, d)) + return bool(cnt1) and bool(cnt2) and cnt1 == cnt2 + + n = len(s) + m = n // 2 + t = s[m:][::-1] + s = s[:m] + pre1 = [[0] * 26 for _ in range(m + 1)] + pre2 = [[0] * 26 for _ in range(m + 1)] + diff = [0] * (m + 1) + for i, (c1, c2) in enumerate(zip(s, t), 1): + pre1[i] = pre1[i - 1][:] + pre2[i] = pre2[i - 1][:] + pre1[i][ord(c1) - ord("a")] += 1 + pre2[i][ord(c2) - ord("a")] += 1 + diff[i] = diff[i - 1] + int(c1 != c2) + ans = [] + for a, b, c, d in queries: + c, d = n - 1 - d, n - 1 - c + ok = ( + check(pre1, pre2, a, b, c, d) + if a <= c + else check(pre2, pre1, c, d, a, b) + ) + ans.append(ok) + return ans ``` ### **Java** @@ -106,19 +179,313 @@ a0 = 1, b0 = 2, c0 = 4, d0 = 5. ```java - +class Solution { + public boolean[] canMakePalindromeQueries(String s, int[][] queries) { + int n = s.length(); + int m = n / 2; + String t = new StringBuilder(s.substring(m)).reverse().toString(); + s = s.substring(0, m); + int[][] pre1 = new int[m + 1][0]; + int[][] pre2 = new int[m + 1][0]; + int[] diff = new int[m + 1]; + pre1[0] = new int[26]; + pre2[0] = new int[26]; + for (int i = 1; i <= m; ++i) { + pre1[i] = pre1[i - 1].clone(); + pre2[i] = pre2[i - 1].clone(); + ++pre1[i][s.charAt(i - 1) - 'a']; + ++pre2[i][t.charAt(i - 1) - 'a']; + diff[i] = diff[i - 1] + (s.charAt(i - 1) == t.charAt(i - 1) ? 0 : 1); + } + boolean[] ans = new boolean[queries.length]; + for (int i = 0; i < queries.length; ++i) { + int[] q = queries[i]; + int a = q[0], b = q[1]; + int c = n - 1 - q[3], d = n - 1 - q[2]; + ans[i] = a <= c ? check(pre1, pre2, diff, a, b, c, d) + : check(pre2, pre1, diff, c, d, a, b); + } + return ans; + } + + private boolean check(int[][] pre1, int[][] pre2, int[] diff, int a, int b, int c, int d) { + if (diff[a] > 0 || diff[diff.length - 1] - diff[Math.max(b, d) + 1] > 0) { + return false; + } + if (d <= b) { + return Arrays.equals(count(pre1, a, b), count(pre2, a, b)); + } + if (b < c) { + return diff[c] - diff[b + 1] == 0 && Arrays.equals(count(pre1, a, b), count(pre2, a, b)) + && Arrays.equals(count(pre1, c, d), count(pre2, c, d)); + } + int[] cnt1 = sub(count(pre1, a, b), count(pre2, a, c - 1)); + int[] cnt2 = sub(count(pre2, c, d), count(pre1, b + 1, d)); + return cnt1 != null && cnt2 != null && Arrays.equals(cnt1, cnt2); + } + + private int[] count(int[][] pre, int i, int j) { + int[] cnt = new int[26]; + for (int k = 0; k < 26; ++k) { + cnt[k] = pre[j + 1][k] - pre[i][k]; + } + return cnt; + } + + private int[] sub(int[] cnt1, int[] cnt2) { + int[] cnt = new int[26]; + for (int i = 0; i < 26; ++i) { + cnt[i] = cnt1[i] - cnt2[i]; + if (cnt[i] < 0) { + return null; + } + } + return cnt; + } +} ``` ### **C++** ```cpp - +class Solution { +public: + vector canMakePalindromeQueries(string s, vector>& queries) { + int n = s.length(); + int m = n / 2; + string t = string(s.begin() + m, s.end()); + reverse(t.begin(), t.end()); + s = string(s.begin(), s.begin() + m); + + vector> pre1(m + 1, vector(26)); + vector> pre2(m + 1, vector(26)); + vector diff(m + 1, 0); + for (int i = 1; i <= m; ++i) { + pre1[i] = pre1[i - 1]; + pre2[i] = pre2[i - 1]; + ++pre1[i][s[i - 1] - 'a']; + ++pre2[i][t[i - 1] - 'a']; + diff[i] = diff[i - 1] + (s[i - 1] == t[i - 1] ? 0 : 1); + } + + vector ans(queries.size(), false); + for (int i = 0; i < queries.size(); ++i) { + vector q = queries[i]; + int a = q[0], b = q[1]; + int c = n - 1 - q[3], d = n - 1 - q[2]; + ans[i] = (a <= c) ? check(pre1, pre2, diff, a, b, c, d) : check(pre2, pre1, diff, c, d, a, b); + } + return ans; + } + +private: + bool check(const vector>& pre1, const vector>& pre2, const vector& diff, int a, int b, int c, int d) { + if (diff[a] > 0 || diff[diff.size() - 1] - diff[max(b, d) + 1] > 0) { + return false; + } + + if (d <= b) { + return count(pre1, a, b) == count(pre2, a, b); + } + + if (b < c) { + return diff[c] - diff[b + 1] == 0 && count(pre1, a, b) == count(pre2, a, b) && count(pre1, c, d) == count(pre2, c, d); + } + + vector cnt1 = sub(count(pre1, a, b), count(pre2, a, c - 1)); + vector cnt2 = sub(count(pre2, c, d), count(pre1, b + 1, d)); + + return cnt1 != vector() && cnt2 != vector() && cnt1 == cnt2; + } + + vector count(const vector>& pre, int i, int j) { + vector cnt(26); + for (int k = 0; k < 26; ++k) { + cnt[k] = pre[j + 1][k] - pre[i][k]; + } + return cnt; + } + + vector sub(const vector& cnt1, const vector& cnt2) { + vector cnt(26); + for (int i = 0; i < 26; ++i) { + cnt[i] = cnt1[i] - cnt2[i]; + if (cnt[i] < 0) { + return vector(); + } + } + return cnt; + } +}; ``` ### **Go** ```go +func canMakePalindromeQueries(s string, queries [][]int) (ans []bool) { + n := len(s) + m := n / 2 + t := reverse(s[m:]) + s = s[:m] + + pre1 := make([][]int, m+1) + pre2 := make([][]int, m+1) + diff := make([]int, m+1) + pre1[0] = make([]int, 26) + pre2[0] = make([]int, 26) + + for i := 1; i <= m; i++ { + pre1[i] = slices.Clone(pre1[i-1]) + pre2[i] = slices.Clone(pre2[i-1]) + pre1[i][int(s[i-1]-'a')]++ + pre2[i][int(t[i-1]-'a')]++ + diff[i] = diff[i-1] + if s[i-1] != t[i-1] { + diff[i]++ + } + } + for _, q := range queries { + a, b := q[0], q[1] + c, d := n-1-q[3], n-1-q[2] + if a <= c { + ans = append(ans, check(pre1, pre2, diff, a, b, c, d)) + } else { + ans = append(ans, check(pre2, pre1, diff, c, d, a, b)) + } + } + return +} + +func check(pre1, pre2 [][]int, diff []int, a, b, c, d int) bool { + if diff[a] > 0 || diff[len(diff)-1]-diff[max(b, d)+1] > 0 { + return false + } + + if d <= b { + return slices.Equal(count(pre1, a, b), count(pre2, a, b)) + } + + if b < c { + return diff[c]-diff[b+1] == 0 && slices.Equal(count(pre1, a, b), count(pre2, a, b)) && slices.Equal(count(pre1, c, d), count(pre2, c, d)) + } + + cnt1 := sub(count(pre1, a, b), count(pre2, a, c-1)) + cnt2 := sub(count(pre2, c, d), count(pre1, b+1, d)) + + return !slices.Equal(cnt1, []int{}) && !slices.Equal(cnt2, []int{}) && slices.Equal(cnt1, cnt2) +} + +func count(pre [][]int, i, j int) []int { + cnt := make([]int, 26) + for k := 0; k < 26; k++ { + cnt[k] = pre[j+1][k] - pre[i][k] + } + return cnt +} + +func sub(cnt1, cnt2 []int) []int { + cnt := make([]int, 26) + for i := 0; i < 26; i++ { + cnt[i] = cnt1[i] - cnt2[i] + if cnt[i] < 0 { + return []int{} + } + } + return cnt +} + +func reverse(s string) string { + runes := []rune(s) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + return string(runes) +} +``` +### **TypeScript** + +```ts +function canMakePalindromeQueries(s: string, queries: number[][]): boolean[] { + const n: number = s.length; + const m: number = n >> 1; + const t: string = s.slice(m).split('').reverse().join(''); + s = s.slice(0, m); + + const pre1: number[][] = Array.from({ length: m + 1 }, () => Array(26).fill(0)); + const pre2: number[][] = Array.from({ length: m + 1 }, () => Array(26).fill(0)); + const diff: number[] = Array(m + 1).fill(0); + for (let i = 1; i <= m; ++i) { + pre1[i] = [...pre1[i - 1]]; + pre2[i] = [...pre2[i - 1]]; + ++pre1[i][s.charCodeAt(i - 1) - 'a'.charCodeAt(0)]; + ++pre2[i][t.charCodeAt(i - 1) - 'a'.charCodeAt(0)]; + diff[i] = diff[i - 1] + (s[i - 1] === t[i - 1] ? 0 : 1); + } + + const ans: boolean[] = Array(queries.length).fill(false); + for (let i = 0; i < queries.length; ++i) { + const q: number[] = queries[i]; + const [a, b] = [q[0], q[1]]; + const [c, d] = [n - 1 - q[3], n - 1 - q[2]]; + ans[i] = a <= c ? check(pre1, pre2, diff, a, b, c, d) : check(pre2, pre1, diff, c, d, a, b); + } + return ans; +} + +function check( + pre1: number[][], + pre2: number[][], + diff: number[], + a: number, + b: number, + c: number, + d: number, +): boolean { + if (diff[a] > 0 || diff[diff.length - 1] - diff[Math.max(b, d) + 1] > 0) { + return false; + } + + if (d <= b) { + return arraysEqual(count(pre1, a, b), count(pre2, a, b)); + } + + if (b < c) { + return ( + diff[c] - diff[b + 1] === 0 && + arraysEqual(count(pre1, a, b), count(pre2, a, b)) && + arraysEqual(count(pre1, c, d), count(pre2, c, d)) + ); + } + + const cnt1: number[] | null = sub(count(pre1, a, b), count(pre2, a, c - 1)); + const cnt2: number[] | null = sub(count(pre2, c, d), count(pre1, b + 1, d)); + + return cnt1 !== null && cnt2 !== null && arraysEqual(cnt1, cnt2); +} + +function count(pre: number[][], i: number, j: number): number[] { + const cnt: number[] = new Array(26).fill(0); + for (let k = 0; k < 26; ++k) { + cnt[k] = pre[j + 1][k] - pre[i][k]; + } + return cnt; +} + +function sub(cnt1: number[], cnt2: number[]): number[] | null { + const cnt: number[] = new Array(26).fill(0); + for (let i = 0; i < 26; ++i) { + cnt[i] = cnt1[i] - cnt2[i]; + if (cnt[i] < 0) { + return null; + } + } + return cnt; +} + +function arraysEqual(arr1: number[], arr2: number[]): boolean { + return JSON.stringify(arr1) === JSON.stringify(arr2); +} ``` ### **...** diff --git a/solution/2900-2999/2983.Palindrome Rearrangement Queries/README_EN.md b/solution/2900-2999/2983.Palindrome Rearrangement Queries/README_EN.md index 49a590da62f17..b0f43aa7fa90b 100644 --- a/solution/2900-2999/2983.Palindrome Rearrangement Queries/README_EN.md +++ b/solution/2900-2999/2983.Palindrome Rearrangement Queries/README_EN.md @@ -85,30 +85,397 @@ Now, s is a palindrome. So, answer[0] = true. ## Solutions +**Solution 1: Prefix Sum + Case Discussion** + +Let's denote the length of string $s$ as $n$, then half of the length is $m = \frac{n}{2}$. Next, we divide string $s$ into two equal-length segments, where the second segment is reversed to get string $t$, and the first segment remains as $s$. For each query $[a_i, b_i, c_i, d_i]$, where $c_i$ and $d_i$ need to be transformed to $n - 1 - d_i$ and $n - 1 - c_i$. The problem is transformed into: for each query $[a_i, b_i, c_i, d_i]$, determine whether $s[a_i, b_i]$ and $t[c_i, d_i]$ can be rearranged to make strings $s$ and $t$ equal. + +We preprocess the following information: + +1. The prefix sum array $pre_1$ of string $s$, where $pre_1[i][j]$ represents the quantity of character $j$ in the first $i$ characters of string $s$; +2. The prefix sum array $pre_2$ of string $t$, where $pre_2[i][j]$ represents the quantity of character $j$ in the first $i$ characters of string $t$; +3. The difference array $diff$ of strings $s$ and $t$, where $diff[i]$ represents the quantity of different characters in the first $i$ characters of strings $s$ and $t$. + +For each query $[a_i, b_i, c_i, d_i]$, let's assume $a_i \le c_i$, then we need to discuss the following cases: + +1. The prefix substrings $s[..a_i-1]$ and $t[..a_i-1]$ of strings $s$ and $t$ must be equal, and the suffix substrings $s[\max(b_i, d_i)+1..]$ and $t[\max(b_i, d_i)..]$ must also be equal, otherwise, it is impossible to rearrange to make strings $s$ and $t$ equal; +2. If $d_i \le b_i$, it means the interval $[a_i, b_i]$ contains the interval $[c_i, d_i]$. If the substrings $s[a_i, b_i]$ and $t[a_i, b_i]$ contain the same quantity of characters, then it is possible to rearrange to make strings $s$ and $t$ equal, otherwise, it is impossible; +3. If $b_i < c_i$, it means the intervals $[a_i, b_i]$ and $[c_i, d_i]$ do not intersect. Then the substrings $s[b_i+1, c_i-1]$ and $t[b_i+1, c_i-1]$ must be equal, and the substrings $s[a_i, b_i]$ and $t[a_i, b_i]$ must be equal, and the substrings $s[c_i, d_i]$ and $t[c_i, d_i]$ must be equal, otherwise, it is impossible to rearrange to make strings $s$ and $t$ equal. +4. If $c_i \le b_i < d_i$, it means the intervals $[a_i, b_i]$ and $[c_i, d_i]$ intersect. Then the characters contained in $s[a_i, b_i]$, minus the characters contained in $t[a_i, c_i-1]$, must be equal to the characters contained in $t[c_i, d_i]$, minus the characters contained in $s[b_i+1, d_i]$, otherwise, it is impossible to rearrange to make strings $s$ and $t$ equal. + +Based on the above analysis, we iterate through each query $[a_i, b_i, c_i, d_i]$, and determine whether it satisfies the above conditions. + +The time complexity is $O((n + q) \times |\Sigma|)$, and the space complexity is $O(n \times |\Sigma|)$. Where $n$ and $q$ are the lengths of string $s$ and the query array $queries$ respectively; and $|\Sigma|$ is the size of the character set. In this problem, the character set is lowercase English letters, so $|\Sigma| = 26$. + ### **Python3** ```python - +class Solution: + def canMakePalindromeQueries(self, s: str, queries: List[List[int]]) -> List[bool]: + def count(pre: List[List[int]], i: int, j: int) -> List[int]: + return [x - y for x, y in zip(pre[j + 1], pre[i])] + + def sub(cnt1: List[int], cnt2: List[int]) -> List[int]: + res = [] + for x, y in zip(cnt1, cnt2): + if x - y < 0: + return [] + res.append(x - y) + return res + + def check( + pre1: List[List[int]], pre2: List[List[int]], a: int, b: int, c: int, d: int + ) -> bool: + if diff[a] > 0 or diff[m] - diff[max(b, d) + 1] > 0: + return False + if d <= b: + return count(pre1, a, b) == count(pre2, a, b) + if b < c: + return ( + diff[c] - diff[b + 1] == 0 + and count(pre1, a, b) == count(pre2, a, b) + and count(pre1, c, d) == count(pre2, c, d) + ) + cnt1 = sub(count(pre1, a, b), count(pre2, a, c - 1)) + cnt2 = sub(count(pre2, c, d), count(pre1, b + 1, d)) + return bool(cnt1) and bool(cnt2) and cnt1 == cnt2 + + n = len(s) + m = n // 2 + t = s[m:][::-1] + s = s[:m] + pre1 = [[0] * 26 for _ in range(m + 1)] + pre2 = [[0] * 26 for _ in range(m + 1)] + diff = [0] * (m + 1) + for i, (c1, c2) in enumerate(zip(s, t), 1): + pre1[i] = pre1[i - 1][:] + pre2[i] = pre2[i - 1][:] + pre1[i][ord(c1) - ord("a")] += 1 + pre2[i][ord(c2) - ord("a")] += 1 + diff[i] = diff[i - 1] + int(c1 != c2) + ans = [] + for a, b, c, d in queries: + c, d = n - 1 - d, n - 1 - c + ok = ( + check(pre1, pre2, a, b, c, d) + if a <= c + else check(pre2, pre1, c, d, a, b) + ) + ans.append(ok) + return ans ``` ### **Java** ```java - +class Solution { + public boolean[] canMakePalindromeQueries(String s, int[][] queries) { + int n = s.length(); + int m = n / 2; + String t = new StringBuilder(s.substring(m)).reverse().toString(); + s = s.substring(0, m); + int[][] pre1 = new int[m + 1][0]; + int[][] pre2 = new int[m + 1][0]; + int[] diff = new int[m + 1]; + pre1[0] = new int[26]; + pre2[0] = new int[26]; + for (int i = 1; i <= m; ++i) { + pre1[i] = pre1[i - 1].clone(); + pre2[i] = pre2[i - 1].clone(); + ++pre1[i][s.charAt(i - 1) - 'a']; + ++pre2[i][t.charAt(i - 1) - 'a']; + diff[i] = diff[i - 1] + (s.charAt(i - 1) == t.charAt(i - 1) ? 0 : 1); + } + boolean[] ans = new boolean[queries.length]; + for (int i = 0; i < queries.length; ++i) { + int[] q = queries[i]; + int a = q[0], b = q[1]; + int c = n - 1 - q[3], d = n - 1 - q[2]; + ans[i] = a <= c ? check(pre1, pre2, diff, a, b, c, d) + : check(pre2, pre1, diff, c, d, a, b); + } + return ans; + } + + private boolean check(int[][] pre1, int[][] pre2, int[] diff, int a, int b, int c, int d) { + if (diff[a] > 0 || diff[diff.length - 1] - diff[Math.max(b, d) + 1] > 0) { + return false; + } + if (d <= b) { + return Arrays.equals(count(pre1, a, b), count(pre2, a, b)); + } + if (b < c) { + return diff[c] - diff[b + 1] == 0 && Arrays.equals(count(pre1, a, b), count(pre2, a, b)) + && Arrays.equals(count(pre1, c, d), count(pre2, c, d)); + } + int[] cnt1 = sub(count(pre1, a, b), count(pre2, a, c - 1)); + int[] cnt2 = sub(count(pre2, c, d), count(pre1, b + 1, d)); + return cnt1 != null && cnt2 != null && Arrays.equals(cnt1, cnt2); + } + + private int[] count(int[][] pre, int i, int j) { + int[] cnt = new int[26]; + for (int k = 0; k < 26; ++k) { + cnt[k] = pre[j + 1][k] - pre[i][k]; + } + return cnt; + } + + private int[] sub(int[] cnt1, int[] cnt2) { + int[] cnt = new int[26]; + for (int i = 0; i < 26; ++i) { + cnt[i] = cnt1[i] - cnt2[i]; + if (cnt[i] < 0) { + return null; + } + } + return cnt; + } +} ``` ### **C++** ```cpp - +class Solution { +public: + vector canMakePalindromeQueries(string s, vector>& queries) { + int n = s.length(); + int m = n / 2; + string t = string(s.begin() + m, s.end()); + reverse(t.begin(), t.end()); + s = string(s.begin(), s.begin() + m); + + vector> pre1(m + 1, vector(26)); + vector> pre2(m + 1, vector(26)); + vector diff(m + 1, 0); + for (int i = 1; i <= m; ++i) { + pre1[i] = pre1[i - 1]; + pre2[i] = pre2[i - 1]; + ++pre1[i][s[i - 1] - 'a']; + ++pre2[i][t[i - 1] - 'a']; + diff[i] = diff[i - 1] + (s[i - 1] == t[i - 1] ? 0 : 1); + } + + vector ans(queries.size(), false); + for (int i = 0; i < queries.size(); ++i) { + vector q = queries[i]; + int a = q[0], b = q[1]; + int c = n - 1 - q[3], d = n - 1 - q[2]; + ans[i] = (a <= c) ? check(pre1, pre2, diff, a, b, c, d) : check(pre2, pre1, diff, c, d, a, b); + } + return ans; + } + +private: + bool check(const vector>& pre1, const vector>& pre2, const vector& diff, int a, int b, int c, int d) { + if (diff[a] > 0 || diff[diff.size() - 1] - diff[max(b, d) + 1] > 0) { + return false; + } + + if (d <= b) { + return count(pre1, a, b) == count(pre2, a, b); + } + + if (b < c) { + return diff[c] - diff[b + 1] == 0 && count(pre1, a, b) == count(pre2, a, b) && count(pre1, c, d) == count(pre2, c, d); + } + + vector cnt1 = sub(count(pre1, a, b), count(pre2, a, c - 1)); + vector cnt2 = sub(count(pre2, c, d), count(pre1, b + 1, d)); + + return cnt1 != vector() && cnt2 != vector() && cnt1 == cnt2; + } + + vector count(const vector>& pre, int i, int j) { + vector cnt(26); + for (int k = 0; k < 26; ++k) { + cnt[k] = pre[j + 1][k] - pre[i][k]; + } + return cnt; + } + + vector sub(const vector& cnt1, const vector& cnt2) { + vector cnt(26); + for (int i = 0; i < 26; ++i) { + cnt[i] = cnt1[i] - cnt2[i]; + if (cnt[i] < 0) { + return vector(); + } + } + return cnt; + } +}; ``` ### **Go** ```go +func canMakePalindromeQueries(s string, queries [][]int) (ans []bool) { + n := len(s) + m := n / 2 + t := reverse(s[m:]) + s = s[:m] + + pre1 := make([][]int, m+1) + pre2 := make([][]int, m+1) + diff := make([]int, m+1) + pre1[0] = make([]int, 26) + pre2[0] = make([]int, 26) + + for i := 1; i <= m; i++ { + pre1[i] = slices.Clone(pre1[i-1]) + pre2[i] = slices.Clone(pre2[i-1]) + pre1[i][int(s[i-1]-'a')]++ + pre2[i][int(t[i-1]-'a')]++ + diff[i] = diff[i-1] + if s[i-1] != t[i-1] { + diff[i]++ + } + } + for _, q := range queries { + a, b := q[0], q[1] + c, d := n-1-q[3], n-1-q[2] + if a <= c { + ans = append(ans, check(pre1, pre2, diff, a, b, c, d)) + } else { + ans = append(ans, check(pre2, pre1, diff, c, d, a, b)) + } + } + return +} + +func check(pre1, pre2 [][]int, diff []int, a, b, c, d int) bool { + if diff[a] > 0 || diff[len(diff)-1]-diff[max(b, d)+1] > 0 { + return false + } + + if d <= b { + return slices.Equal(count(pre1, a, b), count(pre2, a, b)) + } + + if b < c { + return diff[c]-diff[b+1] == 0 && slices.Equal(count(pre1, a, b), count(pre2, a, b)) && slices.Equal(count(pre1, c, d), count(pre2, c, d)) + } + + cnt1 := sub(count(pre1, a, b), count(pre2, a, c-1)) + cnt2 := sub(count(pre2, c, d), count(pre1, b+1, d)) + + return !slices.Equal(cnt1, []int{}) && !slices.Equal(cnt2, []int{}) && slices.Equal(cnt1, cnt2) +} + +func count(pre [][]int, i, j int) []int { + cnt := make([]int, 26) + for k := 0; k < 26; k++ { + cnt[k] = pre[j+1][k] - pre[i][k] + } + return cnt +} + +func sub(cnt1, cnt2 []int) []int { + cnt := make([]int, 26) + for i := 0; i < 26; i++ { + cnt[i] = cnt1[i] - cnt2[i] + if cnt[i] < 0 { + return []int{} + } + } + return cnt +} + +func reverse(s string) string { + runes := []rune(s) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + return string(runes) +} +``` +### **TypeScript** + +```ts +function canMakePalindromeQueries(s: string, queries: number[][]): boolean[] { + const n: number = s.length; + const m: number = n >> 1; + const t: string = s.slice(m).split('').reverse().join(''); + s = s.slice(0, m); + + const pre1: number[][] = Array.from({ length: m + 1 }, () => Array(26).fill(0)); + const pre2: number[][] = Array.from({ length: m + 1 }, () => Array(26).fill(0)); + const diff: number[] = Array(m + 1).fill(0); + for (let i = 1; i <= m; ++i) { + pre1[i] = [...pre1[i - 1]]; + pre2[i] = [...pre2[i - 1]]; + ++pre1[i][s.charCodeAt(i - 1) - 'a'.charCodeAt(0)]; + ++pre2[i][t.charCodeAt(i - 1) - 'a'.charCodeAt(0)]; + diff[i] = diff[i - 1] + (s[i - 1] === t[i - 1] ? 0 : 1); + } + + const ans: boolean[] = Array(queries.length).fill(false); + for (let i = 0; i < queries.length; ++i) { + const q: number[] = queries[i]; + const [a, b] = [q[0], q[1]]; + const [c, d] = [n - 1 - q[3], n - 1 - q[2]]; + ans[i] = a <= c ? check(pre1, pre2, diff, a, b, c, d) : check(pre2, pre1, diff, c, d, a, b); + } + return ans; +} + +function check( + pre1: number[][], + pre2: number[][], + diff: number[], + a: number, + b: number, + c: number, + d: number, +): boolean { + if (diff[a] > 0 || diff[diff.length - 1] - diff[Math.max(b, d) + 1] > 0) { + return false; + } + + if (d <= b) { + return arraysEqual(count(pre1, a, b), count(pre2, a, b)); + } + + if (b < c) { + return ( + diff[c] - diff[b + 1] === 0 && + arraysEqual(count(pre1, a, b), count(pre2, a, b)) && + arraysEqual(count(pre1, c, d), count(pre2, c, d)) + ); + } + + const cnt1: number[] | null = sub(count(pre1, a, b), count(pre2, a, c - 1)); + const cnt2: number[] | null = sub(count(pre2, c, d), count(pre1, b + 1, d)); + + return cnt1 !== null && cnt2 !== null && arraysEqual(cnt1, cnt2); +} + +function count(pre: number[][], i: number, j: number): number[] { + const cnt: number[] = new Array(26).fill(0); + for (let k = 0; k < 26; ++k) { + cnt[k] = pre[j + 1][k] - pre[i][k]; + } + return cnt; +} + +function sub(cnt1: number[], cnt2: number[]): number[] | null { + const cnt: number[] = new Array(26).fill(0); + for (let i = 0; i < 26; ++i) { + cnt[i] = cnt1[i] - cnt2[i]; + if (cnt[i] < 0) { + return null; + } + } + return cnt; +} + +function arraysEqual(arr1: number[], arr2: number[]): boolean { + return JSON.stringify(arr1) === JSON.stringify(arr2); +} ``` ### **...** diff --git a/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.cpp b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.cpp new file mode 100644 index 0000000000000..07070bc1b8618 --- /dev/null +++ b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.cpp @@ -0,0 +1,69 @@ +class Solution { +public: + vector canMakePalindromeQueries(string s, vector>& queries) { + int n = s.length(); + int m = n / 2; + string t = string(s.begin() + m, s.end()); + reverse(t.begin(), t.end()); + s = string(s.begin(), s.begin() + m); + + vector> pre1(m + 1, vector(26)); + vector> pre2(m + 1, vector(26)); + vector diff(m + 1, 0); + for (int i = 1; i <= m; ++i) { + pre1[i] = pre1[i - 1]; + pre2[i] = pre2[i - 1]; + ++pre1[i][s[i - 1] - 'a']; + ++pre2[i][t[i - 1] - 'a']; + diff[i] = diff[i - 1] + (s[i - 1] == t[i - 1] ? 0 : 1); + } + + vector ans(queries.size(), false); + for (int i = 0; i < queries.size(); ++i) { + vector q = queries[i]; + int a = q[0], b = q[1]; + int c = n - 1 - q[3], d = n - 1 - q[2]; + ans[i] = (a <= c) ? check(pre1, pre2, diff, a, b, c, d) : check(pre2, pre1, diff, c, d, a, b); + } + return ans; + } + +private: + bool check(const vector>& pre1, const vector>& pre2, const vector& diff, int a, int b, int c, int d) { + if (diff[a] > 0 || diff[diff.size() - 1] - diff[max(b, d) + 1] > 0) { + return false; + } + + if (d <= b) { + return count(pre1, a, b) == count(pre2, a, b); + } + + if (b < c) { + return diff[c] - diff[b + 1] == 0 && count(pre1, a, b) == count(pre2, a, b) && count(pre1, c, d) == count(pre2, c, d); + } + + vector cnt1 = sub(count(pre1, a, b), count(pre2, a, c - 1)); + vector cnt2 = sub(count(pre2, c, d), count(pre1, b + 1, d)); + + return cnt1 != vector() && cnt2 != vector() && cnt1 == cnt2; + } + + vector count(const vector>& pre, int i, int j) { + vector cnt(26); + for (int k = 0; k < 26; ++k) { + cnt[k] = pre[j + 1][k] - pre[i][k]; + } + return cnt; + } + + vector sub(const vector& cnt1, const vector& cnt2) { + vector cnt(26); + for (int i = 0; i < 26; ++i) { + cnt[i] = cnt1[i] - cnt2[i]; + if (cnt[i] < 0) { + return vector(); + } + } + return cnt; + } +}; \ No newline at end of file diff --git a/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.go b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.go new file mode 100644 index 0000000000000..19021041471ac --- /dev/null +++ b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.go @@ -0,0 +1,79 @@ +func canMakePalindromeQueries(s string, queries [][]int) (ans []bool) { + n := len(s) + m := n / 2 + t := reverse(s[m:]) + s = s[:m] + + pre1 := make([][]int, m+1) + pre2 := make([][]int, m+1) + diff := make([]int, m+1) + pre1[0] = make([]int, 26) + pre2[0] = make([]int, 26) + + for i := 1; i <= m; i++ { + pre1[i] = slices.Clone(pre1[i-1]) + pre2[i] = slices.Clone(pre2[i-1]) + pre1[i][int(s[i-1]-'a')]++ + pre2[i][int(t[i-1]-'a')]++ + diff[i] = diff[i-1] + if s[i-1] != t[i-1] { + diff[i]++ + } + } + for _, q := range queries { + a, b := q[0], q[1] + c, d := n-1-q[3], n-1-q[2] + if a <= c { + ans = append(ans, check(pre1, pre2, diff, a, b, c, d)) + } else { + ans = append(ans, check(pre2, pre1, diff, c, d, a, b)) + } + } + return +} + +func check(pre1, pre2 [][]int, diff []int, a, b, c, d int) bool { + if diff[a] > 0 || diff[len(diff)-1]-diff[max(b, d)+1] > 0 { + return false + } + + if d <= b { + return slices.Equal(count(pre1, a, b), count(pre2, a, b)) + } + + if b < c { + return diff[c]-diff[b+1] == 0 && slices.Equal(count(pre1, a, b), count(pre2, a, b)) && slices.Equal(count(pre1, c, d), count(pre2, c, d)) + } + + cnt1 := sub(count(pre1, a, b), count(pre2, a, c-1)) + cnt2 := sub(count(pre2, c, d), count(pre1, b+1, d)) + + return !slices.Equal(cnt1, []int{}) && !slices.Equal(cnt2, []int{}) && slices.Equal(cnt1, cnt2) +} + +func count(pre [][]int, i, j int) []int { + cnt := make([]int, 26) + for k := 0; k < 26; k++ { + cnt[k] = pre[j+1][k] - pre[i][k] + } + return cnt +} + +func sub(cnt1, cnt2 []int) []int { + cnt := make([]int, 26) + for i := 0; i < 26; i++ { + cnt[i] = cnt1[i] - cnt2[i] + if cnt[i] < 0 { + return []int{} + } + } + return cnt +} + +func reverse(s string) string { + runes := []rune(s) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + return string(runes) +} \ No newline at end of file diff --git a/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.java b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.java new file mode 100644 index 0000000000000..2a794c56a061d --- /dev/null +++ b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.java @@ -0,0 +1,64 @@ +class Solution { + public boolean[] canMakePalindromeQueries(String s, int[][] queries) { + int n = s.length(); + int m = n / 2; + String t = new StringBuilder(s.substring(m)).reverse().toString(); + s = s.substring(0, m); + int[][] pre1 = new int[m + 1][0]; + int[][] pre2 = new int[m + 1][0]; + int[] diff = new int[m + 1]; + pre1[0] = new int[26]; + pre2[0] = new int[26]; + for (int i = 1; i <= m; ++i) { + pre1[i] = pre1[i - 1].clone(); + pre2[i] = pre2[i - 1].clone(); + ++pre1[i][s.charAt(i - 1) - 'a']; + ++pre2[i][t.charAt(i - 1) - 'a']; + diff[i] = diff[i - 1] + (s.charAt(i - 1) == t.charAt(i - 1) ? 0 : 1); + } + boolean[] ans = new boolean[queries.length]; + for (int i = 0; i < queries.length; ++i) { + int[] q = queries[i]; + int a = q[0], b = q[1]; + int c = n - 1 - q[3], d = n - 1 - q[2]; + ans[i] = a <= c ? check(pre1, pre2, diff, a, b, c, d) + : check(pre2, pre1, diff, c, d, a, b); + } + return ans; + } + + private boolean check(int[][] pre1, int[][] pre2, int[] diff, int a, int b, int c, int d) { + if (diff[a] > 0 || diff[diff.length - 1] - diff[Math.max(b, d) + 1] > 0) { + return false; + } + if (d <= b) { + return Arrays.equals(count(pre1, a, b), count(pre2, a, b)); + } + if (b < c) { + return diff[c] - diff[b + 1] == 0 && Arrays.equals(count(pre1, a, b), count(pre2, a, b)) + && Arrays.equals(count(pre1, c, d), count(pre2, c, d)); + } + int[] cnt1 = sub(count(pre1, a, b), count(pre2, a, c - 1)); + int[] cnt2 = sub(count(pre2, c, d), count(pre1, b + 1, d)); + return cnt1 != null && cnt2 != null && Arrays.equals(cnt1, cnt2); + } + + private int[] count(int[][] pre, int i, int j) { + int[] cnt = new int[26]; + for (int k = 0; k < 26; ++k) { + cnt[k] = pre[j + 1][k] - pre[i][k]; + } + return cnt; + } + + private int[] sub(int[] cnt1, int[] cnt2) { + int[] cnt = new int[26]; + for (int i = 0; i < 26; ++i) { + cnt[i] = cnt1[i] - cnt2[i]; + if (cnt[i] < 0) { + return null; + } + } + return cnt; + } +} \ No newline at end of file diff --git a/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.py b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.py new file mode 100644 index 0000000000000..834abc54a1987 --- /dev/null +++ b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.py @@ -0,0 +1,53 @@ +class Solution: + def canMakePalindromeQueries(self, s: str, queries: List[List[int]]) -> List[bool]: + def count(pre: List[List[int]], i: int, j: int) -> List[int]: + return [x - y for x, y in zip(pre[j + 1], pre[i])] + + def sub(cnt1: List[int], cnt2: List[int]) -> List[int]: + res = [] + for x, y in zip(cnt1, cnt2): + if x - y < 0: + return [] + res.append(x - y) + return res + + def check( + pre1: List[List[int]], pre2: List[List[int]], a: int, b: int, c: int, d: int + ) -> bool: + if diff[a] > 0 or diff[m] - diff[max(b, d) + 1] > 0: + return False + if d <= b: + return count(pre1, a, b) == count(pre2, a, b) + if b < c: + return ( + diff[c] - diff[b + 1] == 0 + and count(pre1, a, b) == count(pre2, a, b) + and count(pre1, c, d) == count(pre2, c, d) + ) + cnt1 = sub(count(pre1, a, b), count(pre2, a, c - 1)) + cnt2 = sub(count(pre2, c, d), count(pre1, b + 1, d)) + return bool(cnt1) and bool(cnt2) and cnt1 == cnt2 + + n = len(s) + m = n // 2 + t = s[m:][::-1] + s = s[:m] + pre1 = [[0] * 26 for _ in range(m + 1)] + pre2 = [[0] * 26 for _ in range(m + 1)] + diff = [0] * (m + 1) + for i, (c1, c2) in enumerate(zip(s, t), 1): + pre1[i] = pre1[i - 1][:] + pre2[i] = pre2[i - 1][:] + pre1[i][ord(c1) - ord("a")] += 1 + pre2[i][ord(c2) - ord("a")] += 1 + diff[i] = diff[i - 1] + int(c1 != c2) + ans = [] + for a, b, c, d in queries: + c, d = n - 1 - d, n - 1 - c + ok = ( + check(pre1, pre2, a, b, c, d) + if a <= c + else check(pre2, pre1, c, d, a, b) + ) + ans.append(ok) + return ans diff --git a/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.ts b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.ts new file mode 100644 index 0000000000000..f0d76cfc6c255 --- /dev/null +++ b/solution/2900-2999/2983.Palindrome Rearrangement Queries/Solution.ts @@ -0,0 +1,80 @@ +function canMakePalindromeQueries(s: string, queries: number[][]): boolean[] { + const n: number = s.length; + const m: number = n >> 1; + const t: string = s.slice(m).split('').reverse().join(''); + s = s.slice(0, m); + + const pre1: number[][] = Array.from({ length: m + 1 }, () => Array(26).fill(0)); + const pre2: number[][] = Array.from({ length: m + 1 }, () => Array(26).fill(0)); + const diff: number[] = Array(m + 1).fill(0); + for (let i = 1; i <= m; ++i) { + pre1[i] = [...pre1[i - 1]]; + pre2[i] = [...pre2[i - 1]]; + ++pre1[i][s.charCodeAt(i - 1) - 'a'.charCodeAt(0)]; + ++pre2[i][t.charCodeAt(i - 1) - 'a'.charCodeAt(0)]; + diff[i] = diff[i - 1] + (s[i - 1] === t[i - 1] ? 0 : 1); + } + + const ans: boolean[] = Array(queries.length).fill(false); + for (let i = 0; i < queries.length; ++i) { + const q: number[] = queries[i]; + const [a, b] = [q[0], q[1]]; + const [c, d] = [n - 1 - q[3], n - 1 - q[2]]; + ans[i] = a <= c ? check(pre1, pre2, diff, a, b, c, d) : check(pre2, pre1, diff, c, d, a, b); + } + return ans; +} + +function check( + pre1: number[][], + pre2: number[][], + diff: number[], + a: number, + b: number, + c: number, + d: number, +): boolean { + if (diff[a] > 0 || diff[diff.length - 1] - diff[Math.max(b, d) + 1] > 0) { + return false; + } + + if (d <= b) { + return arraysEqual(count(pre1, a, b), count(pre2, a, b)); + } + + if (b < c) { + return ( + diff[c] - diff[b + 1] === 0 && + arraysEqual(count(pre1, a, b), count(pre2, a, b)) && + arraysEqual(count(pre1, c, d), count(pre2, c, d)) + ); + } + + const cnt1: number[] | null = sub(count(pre1, a, b), count(pre2, a, c - 1)); + const cnt2: number[] | null = sub(count(pre2, c, d), count(pre1, b + 1, d)); + + return cnt1 !== null && cnt2 !== null && arraysEqual(cnt1, cnt2); +} + +function count(pre: number[][], i: number, j: number): number[] { + const cnt: number[] = new Array(26).fill(0); + for (let k = 0; k < 26; ++k) { + cnt[k] = pre[j + 1][k] - pre[i][k]; + } + return cnt; +} + +function sub(cnt1: number[], cnt2: number[]): number[] | null { + const cnt: number[] = new Array(26).fill(0); + for (let i = 0; i < 26; ++i) { + cnt[i] = cnt1[i] - cnt2[i]; + if (cnt[i] < 0) { + return null; + } + } + return cnt; +} + +function arraysEqual(arr1: number[], arr2: number[]): boolean { + return JSON.stringify(arr1) === JSON.stringify(arr2); +} diff --git a/solution/DATABASE_README.md b/solution/DATABASE_README.md index 7a6f535f04216..698c8e029750f 100644 --- a/solution/DATABASE_README.md +++ b/solution/DATABASE_README.md @@ -255,7 +255,7 @@ | 2988 | [Manager of the Largest Department](/solution/2900-2999/2988.Manager%20of%20the%20Largest%20Department/README.md) | | 中等 | 🔒 | | 2989 | [班级表现](/solution/2900-2999/2989.Class%20Performance/README.md) | | 中等 | 🔒 | | 2990 | [贷款类型](/solution/2900-2999/2990.Loan%20Types/README.md) | | 简单 | 🔒 | -| 2991 | [Top Three Wineries](/solution/2900-2999/2991.Top%20Three%20Wineries/README.md) | | 困难 | 🔒 | +| 2991 | [最好的三家酒庄](/solution/2900-2999/2991.Top%20Three%20Wineries/README.md) | | 困难 | 🔒 | ## 版权 diff --git a/solution/README.md b/solution/README.md index 11750f34cfa9a..4c47b82587da1 100644 --- a/solution/README.md +++ b/solution/README.md @@ -3001,7 +3001,7 @@ | 2988 | [Manager of the Largest Department](/solution/2900-2999/2988.Manager%20of%20the%20Largest%20Department/README.md) | | 中等 | 🔒 | | 2989 | [班级表现](/solution/2900-2999/2989.Class%20Performance/README.md) | | 中等 | 🔒 | | 2990 | [贷款类型](/solution/2900-2999/2990.Loan%20Types/README.md) | | 简单 | 🔒 | -| 2991 | [Top Three Wineries](/solution/2900-2999/2991.Top%20Three%20Wineries/README.md) | | 困难 | 🔒 | +| 2991 | [最好的三家酒庄](/solution/2900-2999/2991.Top%20Three%20Wineries/README.md) | | 困难 | 🔒 | ## 版权 diff --git a/solution/database-summary.md b/solution/database-summary.md index 2c51298656512..ab1f1a0734e2b 100644 --- a/solution/database-summary.md +++ b/solution/database-summary.md @@ -245,4 +245,4 @@ - [2988.Manager of the Largest Department](/database-solution/2900-2999/2988.Manager%20of%20the%20Largest%20Department/README.md) - [2989.班级表现](/database-solution/2900-2999/2989.Class%20Performance/README.md) - [2990.贷款类型](/database-solution/2900-2999/2990.Loan%20Types/README.md) - - [2991.Top Three Wineries](/database-solution/2900-2999/2991.Top%20Three%20Wineries/README.md) + - [2991.最好的三家酒庄](/database-solution/2900-2999/2991.Top%20Three%20Wineries/README.md) diff --git a/solution/summary.md b/solution/summary.md index cf349b2aca52f..4f31e3c8af5ed 100644 --- a/solution/summary.md +++ b/solution/summary.md @@ -3048,4 +3048,4 @@ - [2988.Manager of the Largest Department](/solution/2900-2999/2988.Manager%20of%20the%20Largest%20Department/README.md) - [2989.班级表现](/solution/2900-2999/2989.Class%20Performance/README.md) - [2990.贷款类型](/solution/2900-2999/2990.Loan%20Types/README.md) - - [2991.Top Three Wineries](/solution/2900-2999/2991.Top%20Three%20Wineries/README.md) + - [2991.最好的三家酒庄](/solution/2900-2999/2991.Top%20Three%20Wineries/README.md)