diff --git a/solution/0900-0999/0913.Cat and Mouse/README_EN.md b/solution/0900-0999/0913.Cat and Mouse/README_EN.md index da36085b522e9..584bb56a23d44 100644 --- a/solution/0900-0999/0913.Cat and Mouse/README_EN.md +++ b/solution/0900-0999/0913.Cat and Mouse/README_EN.md @@ -80,13 +80,30 @@ tags: -### Solution 1: Greatest Common Divisor +### Solution 1: Topological Sorting -First, we use an array or hash table `cnt` to count the occurrence of each number. Only when $X$ is a divisor of the greatest common divisor of all `cnt[i]`, can it satisfy the problem's requirement. +In the game of cat and mouse, the state is determined by three factors: the position of the mouse, the position of the cat, and the mover. According to the game rules, the boundary states that can directly determine the outcome are: -Therefore, we find the greatest common divisor $g$ of the occurrence of all numbers, and then check whether it is greater than or equal to $2$. +- When the positions of the cat and the mouse are the same, the cat wins. This is a must-win state for the cat and a must-lose state for the mouse. +- When the mouse is in the hole, the mouse wins. This is a must-win state for the mouse and a must-lose state for the cat. -The time complexity is $O(n \times \log M)$, and the space complexity is $O(n + \log M)$. Where $n$ and $M$ are the length of the array `deck` and the maximum value in the array `deck`, respectively. +To get the game result of the initial state, we need to traverse all states starting from the boundary state. Each state includes the position of the mouse, the position of the cat, and the mover. Based on the current state, we can get all possible states of the previous round. The mover of the previous round is opposite to the mover of the current state, and the position of the mover of the previous round is different from the position of the current state. + +We use the tuple $(m, c, t)$ to represent the state of this round, and $(pm, pc, pt)$ to represent the possible state of the previous round. Then, all possible states of the previous round are: + +- If the mover of this round is the mouse, then the mover of the previous round is the cat, the position of the mouse in the previous round is the position of the mouse in this round, and the position of the cat in the previous round is all adjacent points of the position of the cat in this round. +- If the mover of this round is the cat, then the mover of the previous round is the mouse, the position of the cat in the previous round is the position of the cat in this round, and the position of the mouse in the previous round is all adjacent points of the position of the mouse in this round. + +Initially, except for the boundary states, the results of all other states are unknown. We start from the boundary state, for each state, get all possible states of the previous round and update the result. The update logic is as follows: + +1. If the mover of the previous round is the same as the winner of this round, then the mover of the previous round can reach the current state and win, directly update the state of the previous round to the winner of this round. +1. If the mover of the previous round is different from the winner of this round, and all states that the mover of the previous round can reach are the must-lose states for the mover of the previous round, then we update the state of the previous round to the winner of this round. + +For the second update logic, we need to record the degree of each state. Initially, the degree of each state represents the number of nodes that the mover of the state can move to, that is, the number of adjacent nodes of the node where the mover is located. If the mover is the cat and the node where it is located is adjacent to the hole, the degree of the state needs to be reduced by $1$. + +When the results of all states are updated, the result of the initial state is the final result. + +The time complexity is $O(n^3)$, and the space complexity is $O(n^2)$. Where $n$ is the number of nodes in the graph. diff --git a/solution/0900-0999/0920.Number of Music Playlists/README.md b/solution/0900-0999/0920.Number of Music Playlists/README.md index 70cdde769ef88..e58a096ecf82e 100644 --- a/solution/0900-0999/0920.Number of Music Playlists/README.md +++ b/solution/0900-0999/0920.Number of Music Playlists/README.md @@ -85,8 +85,6 @@ $$ 时间复杂度 $O(goal \times n)$,空间复杂度 $O(goal \times n)$。其中 $goal$ 和 $n$ 为题目中给定的参数。 -注意到 $f[i][j]$ 只与 $f[i - 1][j - 1]$ 和 $f[i - 1][j]$ 有关,因此我们可以使用滚动数组优化空间复杂度,时间复杂度不变。 - #### Python3 @@ -235,7 +233,9 @@ impl Solution { -### 方法二 +### 方法二:动态规划(空间优化) + +我们注意到 $f[i][j]$ 只与 $f[i - 1][j - 1]$ 和 $f[i - 1][j]$ 有关,因此我们可以使用滚动数组优化空间复杂度,将空间复杂度优化至 $O(n)$。 diff --git a/solution/0900-0999/0920.Number of Music Playlists/README_EN.md b/solution/0900-0999/0920.Number of Music Playlists/README_EN.md index d2faa909118bc..7e0dfa039b811 100644 --- a/solution/0900-0999/0920.Number of Music Playlists/README_EN.md +++ b/solution/0900-0999/0920.Number of Music Playlists/README_EN.md @@ -83,8 +83,6 @@ The final answer is $f[goal][n]$. The time complexity is $O(goal \times n)$, and the space complexity is $O(goal \times n)$. Here, $goal$ and $n$ are the parameters given in the problem. -Notice that $f[i][j]$ only depends on $f[i - 1][j - 1]$ and $f[i - 1][j]$, so we can use rolling array to optimize the space complexity. The time complexity is unchanged. - #### Python3 @@ -233,7 +231,9 @@ impl Solution { -### Solution 2 +### Solution 2: Dynamic Programming (Space Optimization) + +We notice that $f[i][j]$ is only related to $f[i - 1][j - 1]$ and $f[i - 1][j]$. Therefore, we can use a rolling array to optimize the space complexity, reducing the space complexity to $O(n)$. diff --git a/solution/0900-0999/0925.Long Pressed Name/README.md b/solution/0900-0999/0925.Long Pressed Name/README.md index 1e9efdfebd0f5..12ec097abfb5b 100644 --- a/solution/0900-0999/0925.Long Pressed Name/README.md +++ b/solution/0900-0999/0925.Long Pressed Name/README.md @@ -56,6 +56,10 @@ tags: ### 方法一:双指针 +我们利用两个指针 $i$ 和 $j$ 分别指向字符串 $\text{typed}$ 和 $\text{name}$ 的第一个字符,然后开始遍历,如果 $\text{typed}[j] \neq \text{name}[i]$,说明两个字符串不匹配,直接返回 $\text{False}$。否则,我们找到连续相同的字符的下一个位置,分别记为 $x$ 和 $y$,如果 $x - i > y - j$,说明 $\text{typed}$ 中的字符个数小于 $\text{name}$ 中的字符个数,直接返回 $\text{False}$。否则,我们将 $i$ 和 $j$ 更新为 $x$ 和 $y$,继续遍历,直到 $i$ 和 $j$ 分别遍历完 $\text{name}$ 和 $\text{typed}$,返回 $\text{True}$。 + +时间复杂度 $O(m + n)$,其中 $m$ 和 $n$ 分别是字符串 $\text{name}$ 和 $\text{typed}$ 的长度。空间复杂度 $O(1)$。 + #### Python3 @@ -68,17 +72,15 @@ class Solution: while i < m and j < n: if name[i] != typed[j]: return False - cnt1 = cnt2 = 0 - c = name[i] - while i + 1 < m and name[i + 1] == c: - i += 1 - cnt1 += 1 - while j + 1 < n and typed[j + 1] == c: - j += 1 - cnt2 += 1 - if cnt1 > cnt2: + x = i + 1 + while x < m and name[x] == name[i]: + x += 1 + y = j + 1 + while y < n and typed[y] == typed[j]: + y += 1 + if x - i > y - j: return False - i, j = i + 1, j + 1 + i, j = x, y return i == m and j == n ``` @@ -89,23 +91,23 @@ class Solution { public boolean isLongPressedName(String name, String typed) { int m = name.length(), n = typed.length(); int i = 0, j = 0; - for (; i < m && j < n; ++i, ++j) { + while (i < m && j < n) { if (name.charAt(i) != typed.charAt(j)) { return false; } - int cnt1 = 0, cnt2 = 0; - char c = name.charAt(i); - while (i + 1 < m && name.charAt(i + 1) == c) { - ++i; - ++cnt1; + int x = i + 1; + while (x < m && name.charAt(x) == name.charAt(i)) { + ++x; } - while (j + 1 < n && typed.charAt(j + 1) == c) { - ++j; - ++cnt2; + int y = j + 1; + while (y < n && typed.charAt(y) == typed.charAt(j)) { + ++y; } - if (cnt1 > cnt2) { + if (x - i > y - j) { return false; } + i = x; + j = y; } return i == m && j == n; } @@ -118,21 +120,25 @@ class Solution { class Solution { public: bool isLongPressedName(string name, string typed) { - int m = name.size(), n = typed.size(); + int m = name.length(), n = typed.length(); int i = 0, j = 0; - for (; i < m && j < n; ++i, ++j) { - if (name[i] != typed[j]) return false; - int cnt1 = 0, cnt2 = 0; - char c = name[i]; - while (i + 1 < m && name[i + 1] == c) { - ++i; - ++cnt1; + while (i < m && j < n) { + if (name[i] != typed[j]) { + return false; + } + int x = i + 1; + while (x < m && name[x] == name[i]) { + ++x; } - while (j + 1 < n && typed[j + 1] == c) { - ++j; - ++cnt2; + int y = j + 1; + while (y < n && typed[y] == typed[j]) { + ++y; } - if (cnt1 > cnt2) return false; + if (x - i > y - j) { + return false; + } + i = x; + j = y; } return i == m && j == n; } @@ -145,28 +151,95 @@ public: func isLongPressedName(name string, typed string) bool { m, n := len(name), len(typed) i, j := 0, 0 - for ; i < m && j < n; i, j = i+1, j+1 { + + for i < m && j < n { if name[i] != typed[j] { return false } - cnt1, cnt2 := 0, 0 - c := name[i] - for i+1 < m && name[i+1] == c { - i++ - cnt1++ + x, y := i+1, j+1 + + for x < m && name[x] == name[i] { + x++ } - for j+1 < n && typed[j+1] == c { - j++ - cnt2++ + + for y < n && typed[y] == typed[j] { + y++ } - if cnt1 > cnt2 { + + if x-i > y-j { return false } + + i, j = x, y } + return i == m && j == n } ``` +#### TypeScript + +```ts +function isLongPressedName(name: string, typed: string): boolean { + const [m, n] = [name.length, typed.length]; + let i = 0; + let j = 0; + while (i < m && j < n) { + if (name[i] !== typed[j]) { + return false; + } + let x = i + 1; + while (x < m && name[x] === name[i]) { + x++; + } + let y = j + 1; + while (y < n && typed[y] === typed[j]) { + y++; + } + if (x - i > y - j) { + return false; + } + i = x; + j = y; + } + return i === m && j === n; +} +``` + +#### Rust + +```rust +impl Solution { + pub fn is_long_pressed_name(name: String, typed: String) -> bool { + let (m, n) = (name.len(), typed.len()); + let (mut i, mut j) = (0, 0); + let s: Vec = name.chars().collect(); + let t: Vec = typed.chars().collect(); + + while i < m && j < n { + if s[i] != t[j] { + return false; + } + let mut x = i + 1; + while x < m && s[x] == s[i] { + x += 1; + } + let mut y = j + 1; + while y < n && t[y] == t[j] { + y += 1; + } + if x - i > y - j { + return false; + } + i = x; + j = y; + } + + i == m && j == n + } +} +``` + diff --git a/solution/0900-0999/0925.Long Pressed Name/README_EN.md b/solution/0900-0999/0925.Long Pressed Name/README_EN.md index e86ae37a0c57e..be603ea243547 100644 --- a/solution/0900-0999/0925.Long Pressed Name/README_EN.md +++ b/solution/0900-0999/0925.Long Pressed Name/README_EN.md @@ -52,7 +52,11 @@ tags: -### Solution 1 +### Solution 1: Two Pointers + +We use two pointers $i$ and $j$ to point to the first character of the strings `typed` and `name` respectively, and then start traversing. If `typed[j]` is not equal to `name[i]`, it means the two strings do not match, and we directly return `False`. Otherwise, we find the next position of the continuous identical characters, denoted as $x$ and $y$ respectively. If $x - i > y - j$, it means the number of characters in `typed` is less than the number of characters in `name`, and we directly return `False`. Otherwise, we update $i$ and $j$ to $x$ and $y$ respectively, continue traversing, until $i$ and $j$ have traversed `name` and `typed` respectively, and return `True`. + +The time complexity is $O(m + n)$, where $m$ and $n$ are the lengths of the strings `name` and `typed` respectively. The space complexity is $O(1)`. @@ -66,17 +70,15 @@ class Solution: while i < m and j < n: if name[i] != typed[j]: return False - cnt1 = cnt2 = 0 - c = name[i] - while i + 1 < m and name[i + 1] == c: - i += 1 - cnt1 += 1 - while j + 1 < n and typed[j + 1] == c: - j += 1 - cnt2 += 1 - if cnt1 > cnt2: + x = i + 1 + while x < m and name[x] == name[i]: + x += 1 + y = j + 1 + while y < n and typed[y] == typed[j]: + y += 1 + if x - i > y - j: return False - i, j = i + 1, j + 1 + i, j = x, y return i == m and j == n ``` @@ -87,23 +89,23 @@ class Solution { public boolean isLongPressedName(String name, String typed) { int m = name.length(), n = typed.length(); int i = 0, j = 0; - for (; i < m && j < n; ++i, ++j) { + while (i < m && j < n) { if (name.charAt(i) != typed.charAt(j)) { return false; } - int cnt1 = 0, cnt2 = 0; - char c = name.charAt(i); - while (i + 1 < m && name.charAt(i + 1) == c) { - ++i; - ++cnt1; + int x = i + 1; + while (x < m && name.charAt(x) == name.charAt(i)) { + ++x; } - while (j + 1 < n && typed.charAt(j + 1) == c) { - ++j; - ++cnt2; + int y = j + 1; + while (y < n && typed.charAt(y) == typed.charAt(j)) { + ++y; } - if (cnt1 > cnt2) { + if (x - i > y - j) { return false; } + i = x; + j = y; } return i == m && j == n; } @@ -116,21 +118,25 @@ class Solution { class Solution { public: bool isLongPressedName(string name, string typed) { - int m = name.size(), n = typed.size(); + int m = name.length(), n = typed.length(); int i = 0, j = 0; - for (; i < m && j < n; ++i, ++j) { - if (name[i] != typed[j]) return false; - int cnt1 = 0, cnt2 = 0; - char c = name[i]; - while (i + 1 < m && name[i + 1] == c) { - ++i; - ++cnt1; + while (i < m && j < n) { + if (name[i] != typed[j]) { + return false; + } + int x = i + 1; + while (x < m && name[x] == name[i]) { + ++x; } - while (j + 1 < n && typed[j + 1] == c) { - ++j; - ++cnt2; + int y = j + 1; + while (y < n && typed[y] == typed[j]) { + ++y; } - if (cnt1 > cnt2) return false; + if (x - i > y - j) { + return false; + } + i = x; + j = y; } return i == m && j == n; } @@ -143,28 +149,95 @@ public: func isLongPressedName(name string, typed string) bool { m, n := len(name), len(typed) i, j := 0, 0 - for ; i < m && j < n; i, j = i+1, j+1 { + + for i < m && j < n { if name[i] != typed[j] { return false } - cnt1, cnt2 := 0, 0 - c := name[i] - for i+1 < m && name[i+1] == c { - i++ - cnt1++ + x, y := i+1, j+1 + + for x < m && name[x] == name[i] { + x++ } - for j+1 < n && typed[j+1] == c { - j++ - cnt2++ + + for y < n && typed[y] == typed[j] { + y++ } - if cnt1 > cnt2 { + + if x-i > y-j { return false } + + i, j = x, y } + return i == m && j == n } ``` +#### TypeScript + +```ts +function isLongPressedName(name: string, typed: string): boolean { + const [m, n] = [name.length, typed.length]; + let i = 0; + let j = 0; + while (i < m && j < n) { + if (name[i] !== typed[j]) { + return false; + } + let x = i + 1; + while (x < m && name[x] === name[i]) { + x++; + } + let y = j + 1; + while (y < n && typed[y] === typed[j]) { + y++; + } + if (x - i > y - j) { + return false; + } + i = x; + j = y; + } + return i === m && j === n; +} +``` + +#### Rust + +```rust +impl Solution { + pub fn is_long_pressed_name(name: String, typed: String) -> bool { + let (m, n) = (name.len(), typed.len()); + let (mut i, mut j) = (0, 0); + let s: Vec = name.chars().collect(); + let t: Vec = typed.chars().collect(); + + while i < m && j < n { + if s[i] != t[j] { + return false; + } + let mut x = i + 1; + while x < m && s[x] == s[i] { + x += 1; + } + let mut y = j + 1; + while y < n && t[y] == t[j] { + y += 1; + } + if x - i > y - j { + return false; + } + i = x; + j = y; + } + + i == m && j == n + } +} +``` + diff --git a/solution/0900-0999/0925.Long Pressed Name/Solution.cpp b/solution/0900-0999/0925.Long Pressed Name/Solution.cpp index 2ad6af2ef0fd5..1d4ee0dfd7f40 100644 --- a/solution/0900-0999/0925.Long Pressed Name/Solution.cpp +++ b/solution/0900-0999/0925.Long Pressed Name/Solution.cpp @@ -1,21 +1,25 @@ class Solution { public: bool isLongPressedName(string name, string typed) { - int m = name.size(), n = typed.size(); + int m = name.length(), n = typed.length(); int i = 0, j = 0; - for (; i < m && j < n; ++i, ++j) { - if (name[i] != typed[j]) return false; - int cnt1 = 0, cnt2 = 0; - char c = name[i]; - while (i + 1 < m && name[i + 1] == c) { - ++i; - ++cnt1; + while (i < m && j < n) { + if (name[i] != typed[j]) { + return false; } - while (j + 1 < n && typed[j + 1] == c) { - ++j; - ++cnt2; + int x = i + 1; + while (x < m && name[x] == name[i]) { + ++x; } - if (cnt1 > cnt2) return false; + int y = j + 1; + while (y < n && typed[y] == typed[j]) { + ++y; + } + if (x - i > y - j) { + return false; + } + i = x; + j = y; } return i == m && j == n; } diff --git a/solution/0900-0999/0925.Long Pressed Name/Solution.go b/solution/0900-0999/0925.Long Pressed Name/Solution.go index 5345f79529822..72372ce5657b0 100644 --- a/solution/0900-0999/0925.Long Pressed Name/Solution.go +++ b/solution/0900-0999/0925.Long Pressed Name/Solution.go @@ -1,23 +1,27 @@ func isLongPressedName(name string, typed string) bool { m, n := len(name), len(typed) i, j := 0, 0 - for ; i < m && j < n; i, j = i+1, j+1 { + + for i < m && j < n { if name[i] != typed[j] { return false } - cnt1, cnt2 := 0, 0 - c := name[i] - for i+1 < m && name[i+1] == c { - i++ - cnt1++ + x, y := i+1, j+1 + + for x < m && name[x] == name[i] { + x++ } - for j+1 < n && typed[j+1] == c { - j++ - cnt2++ + + for y < n && typed[y] == typed[j] { + y++ } - if cnt1 > cnt2 { + + if x-i > y-j { return false } + + i, j = x, y } + return i == m && j == n } \ No newline at end of file diff --git a/solution/0900-0999/0925.Long Pressed Name/Solution.java b/solution/0900-0999/0925.Long Pressed Name/Solution.java index 21c892f55ff19..a4f45283f66a9 100644 --- a/solution/0900-0999/0925.Long Pressed Name/Solution.java +++ b/solution/0900-0999/0925.Long Pressed Name/Solution.java @@ -2,23 +2,23 @@ class Solution { public boolean isLongPressedName(String name, String typed) { int m = name.length(), n = typed.length(); int i = 0, j = 0; - for (; i < m && j < n; ++i, ++j) { + while (i < m && j < n) { if (name.charAt(i) != typed.charAt(j)) { return false; } - int cnt1 = 0, cnt2 = 0; - char c = name.charAt(i); - while (i + 1 < m && name.charAt(i + 1) == c) { - ++i; - ++cnt1; + int x = i + 1; + while (x < m && name.charAt(x) == name.charAt(i)) { + ++x; } - while (j + 1 < n && typed.charAt(j + 1) == c) { - ++j; - ++cnt2; + int y = j + 1; + while (y < n && typed.charAt(y) == typed.charAt(j)) { + ++y; } - if (cnt1 > cnt2) { + if (x - i > y - j) { return false; } + i = x; + j = y; } return i == m && j == n; } diff --git a/solution/0900-0999/0925.Long Pressed Name/Solution.py b/solution/0900-0999/0925.Long Pressed Name/Solution.py index 68297c2170a5b..574422a00db86 100644 --- a/solution/0900-0999/0925.Long Pressed Name/Solution.py +++ b/solution/0900-0999/0925.Long Pressed Name/Solution.py @@ -5,15 +5,13 @@ def isLongPressedName(self, name: str, typed: str) -> bool: while i < m and j < n: if name[i] != typed[j]: return False - cnt1 = cnt2 = 0 - c = name[i] - while i + 1 < m and name[i + 1] == c: - i += 1 - cnt1 += 1 - while j + 1 < n and typed[j + 1] == c: - j += 1 - cnt2 += 1 - if cnt1 > cnt2: + x = i + 1 + while x < m and name[x] == name[i]: + x += 1 + y = j + 1 + while y < n and typed[y] == typed[j]: + y += 1 + if x - i > y - j: return False - i, j = i + 1, j + 1 + i, j = x, y return i == m and j == n diff --git a/solution/0900-0999/0925.Long Pressed Name/Solution.rs b/solution/0900-0999/0925.Long Pressed Name/Solution.rs new file mode 100644 index 0000000000000..9bddac708dc36 --- /dev/null +++ b/solution/0900-0999/0925.Long Pressed Name/Solution.rs @@ -0,0 +1,29 @@ +impl Solution { + pub fn is_long_pressed_name(name: String, typed: String) -> bool { + let (m, n) = (name.len(), typed.len()); + let (mut i, mut j) = (0, 0); + let s: Vec = name.chars().collect(); + let t: Vec = typed.chars().collect(); + + while i < m && j < n { + if s[i] != t[j] { + return false; + } + let mut x = i + 1; + while x < m && s[x] == s[i] { + x += 1; + } + let mut y = j + 1; + while y < n && t[y] == t[j] { + y += 1; + } + if x - i > y - j { + return false; + } + i = x; + j = y; + } + + i == m && j == n + } +} diff --git a/solution/0900-0999/0925.Long Pressed Name/Solution.ts b/solution/0900-0999/0925.Long Pressed Name/Solution.ts new file mode 100644 index 0000000000000..5fb34c702a0af --- /dev/null +++ b/solution/0900-0999/0925.Long Pressed Name/Solution.ts @@ -0,0 +1,24 @@ +function isLongPressedName(name: string, typed: string): boolean { + const [m, n] = [name.length, typed.length]; + let i = 0; + let j = 0; + while (i < m && j < n) { + if (name[i] !== typed[j]) { + return false; + } + let x = i + 1; + while (x < m && name[x] === name[i]) { + x++; + } + let y = j + 1; + while (y < n && typed[y] === typed[j]) { + y++; + } + if (x - i > y - j) { + return false; + } + i = x; + j = y; + } + return i === m && j === n; +}