Skip to content

Commit e51cd08

Browse files
authored
feat: add rust solution to lc problem: No.3579 (#4482)
No.3579.Minimum Steps to Convert String with Operations
1 parent 22c6a2b commit e51cd08

File tree

3 files changed

+158
-2
lines changed

3 files changed

+158
-2
lines changed

solution/3500-3599/3579.Minimum Steps to Convert String with Operations/README.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,17 @@ tags:
144144

145145
<!-- solution:start -->
146146

147-
### 方法一
147+
### 方法一:贪心 + 动态规划
148+
149+
我们定义 $f[i]$ 表示将 $\textit{word1}$ 的前 $i$ 个字符转换为 $\textit{word2}$ 的前 $i$ 个字符所需的最小操作数。那么答案为 $f[n]$,其中 $n$ 是 $\textit{word1}$ 和 $\textit{word2}$ 的长度。
150+
151+
我们可以通过遍历所有可能的分割点来计算 $f[i]$。对于每个分割点 $j$,我们需要计算将 $\textit{word1}[j:i]$ 转换为 $\textit{word2}[j:i]$ 所需的最小操作数。
152+
153+
我们可以使用一个辅助函数 $\text{calc}(l, r, \text{rev})$ 来计算从 $\textit{word1}[l:r]$ 转换为 $\textit{word2}[l:r]$ 所需的最小操作数,其中 $\text{rev}$ 表示是否需要反转子串。由于反转前后进行其它操作的结果是一样的,所以我们可以考虑不反转,以及首先进行一次反转后再进行其它操作。因此有 $f[i] = \min_{j < i} (f[j] + \min(\text{calc}(j, i-1, \text{false}), 1 + \text{calc}(j, i-1, \text{true})))$。
154+
155+
接下来我们需要实现 $\text{calc}(l, r, \text{rev})$ 函数。我们用一个二维数组 $cnt$ 来记录 $\textit{word1}$ 和 $\textit{word2}$ 中字符的配对情况。对于每个字符对 $(a, b)$,如果 $a \neq b$,我们需要检查 $cnt[b][a]$ 是否大于 $0$。如果是,我们可以将其配对,减少一次操作;否则,我们需要增加一次操作,并将 $cnt[a][b]$ 加 $1$。
156+
157+
时间复杂度 $O(n^3 + |\Sigma|^2)$,空间复杂度 $O(n + |\Sigma|^2)$,其中 $n$ 是字符串的长度,而 $|\Sigma|$ 是字符集大小(本题中为 $26$)。
148158

149159
<!-- tabs:start -->
150160

@@ -357,6 +367,53 @@ function minOperations(word1: string, word2: string): number {
357367
}
358368
```
359369

370+
#### Rust
371+
372+
```rust
373+
impl Solution {
374+
pub fn min_operations(word1: String, word2: String) -> i32 {
375+
let n = word1.len();
376+
let word1 = word1.as_bytes();
377+
let word2 = word2.as_bytes();
378+
let mut f = vec![i32::MAX; n + 1];
379+
f[0] = 0;
380+
381+
for i in 1..=n {
382+
for j in 0..i {
383+
let a = Self::calc(word1, word2, j, i - 1, false);
384+
let b = 1 + Self::calc(word1, word2, j, i - 1, true);
385+
let t = a.min(b);
386+
f[i] = f[i].min(f[j] + t);
387+
}
388+
}
389+
390+
f[n]
391+
}
392+
393+
fn calc(word1: &[u8], word2: &[u8], l: usize, r: usize, rev: bool) -> i32 {
394+
let mut cnt = [[0i32; 26]; 26];
395+
let mut res = 0;
396+
397+
for i in l..=r {
398+
let j = if rev { r - (i - l) } else { i };
399+
let a = (word1[j] - b'a') as usize;
400+
let b = (word2[i] - b'a') as usize;
401+
402+
if a != b {
403+
if cnt[b][a] > 0 {
404+
cnt[b][a] -= 1;
405+
} else {
406+
cnt[a][b] += 1;
407+
res += 1;
408+
}
409+
}
410+
}
411+
412+
res
413+
}
414+
}
415+
```
416+
360417
<!-- tabs:end -->
361418

362419
<!-- solution:end -->

solution/3500-3599/3579.Minimum Steps to Convert String with Operations/README_EN.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,17 @@ tags:
139139

140140
<!-- solution:start -->
141141

142-
### Solution 1
142+
### Solution 1: Greedy + Dynamic Programming
143+
144+
We define $f[i]$ as the minimum number of operations required to convert the first $i$ characters of $\textit{word1}$ to the first $i$ characters of $\textit{word2}$. The answer is $f[n]$, where $n$ is the length of both $\textit{word1}$ and $\textit{word2}$.
145+
146+
We can compute $f[i]$ by enumerating all possible split points. For each split point $j$, we need to calculate the minimum number of operations required to convert $\textit{word1}[j:i]$ to $\textit{word2}[j:i]$.
147+
148+
We can use a helper function $\text{calc}(l, r, \text{rev})$ to compute the minimum number of operations needed to convert $\textit{word1}[l:r]$ to $\textit{word2}[l:r]$, where $\text{rev}$ indicates whether to reverse the substring. Since the result of performing other operations before or after a reversal is the same, we only need to consider not reversing, and reversing once before other operations. Therefore, $f[i] = \min_{j < i} (f[j] + \min(\text{calc}(j, i-1, \text{false}), 1 + \text{calc}(j, i-1, \text{true})))$.
149+
150+
Next, we need to implement the $\text{calc}(l, r, \text{rev})$ function. We use a 2D array $cnt$ to record the pairing status of characters between $\textit{word1}$ and $\textit{word2}$. For each character pair $(a, b)$, if $a \neq b$, we check whether $cnt[b][a] > 0$. If so, we can pair them and reduce one operation; otherwise, we need to add one operation and increment $cnt[a][b]$ by $1$.
151+
152+
The time complexity is $O(n^3 + |\Sigma|^2)$ and the space complexity is $O(n + |\Sigma|^2)$, where $n$ is the length of the string and $|\Sigma|$ is the size of the character set (which is $26$ in this problem).
143153

144154
<!-- tabs:start -->
145155

@@ -352,6 +362,53 @@ function minOperations(word1: string, word2: string): number {
352362
}
353363
```
354364

365+
#### Rust
366+
367+
```rust
368+
impl Solution {
369+
pub fn min_operations(word1: String, word2: String) -> i32 {
370+
let n = word1.len();
371+
let word1 = word1.as_bytes();
372+
let word2 = word2.as_bytes();
373+
let mut f = vec![i32::MAX; n + 1];
374+
f[0] = 0;
375+
376+
for i in 1..=n {
377+
for j in 0..i {
378+
let a = Self::calc(word1, word2, j, i - 1, false);
379+
let b = 1 + Self::calc(word1, word2, j, i - 1, true);
380+
let t = a.min(b);
381+
f[i] = f[i].min(f[j] + t);
382+
}
383+
}
384+
385+
f[n]
386+
}
387+
388+
fn calc(word1: &[u8], word2: &[u8], l: usize, r: usize, rev: bool) -> i32 {
389+
let mut cnt = [[0i32; 26]; 26];
390+
let mut res = 0;
391+
392+
for i in l..=r {
393+
let j = if rev { r - (i - l) } else { i };
394+
let a = (word1[j] - b'a') as usize;
395+
let b = (word2[i] - b'a') as usize;
396+
397+
if a != b {
398+
if cnt[b][a] > 0 {
399+
cnt[b][a] -= 1;
400+
} else {
401+
cnt[a][b] += 1;
402+
res += 1;
403+
}
404+
}
405+
}
406+
407+
res
408+
}
409+
}
410+
```
411+
355412
<!-- tabs:end -->
356413

357414
<!-- solution:end -->
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
impl Solution {
2+
pub fn min_operations(word1: String, word2: String) -> i32 {
3+
let n = word1.len();
4+
let word1 = word1.as_bytes();
5+
let word2 = word2.as_bytes();
6+
let mut f = vec![i32::MAX; n + 1];
7+
f[0] = 0;
8+
9+
for i in 1..=n {
10+
for j in 0..i {
11+
let a = Self::calc(word1, word2, j, i - 1, false);
12+
let b = 1 + Self::calc(word1, word2, j, i - 1, true);
13+
let t = a.min(b);
14+
f[i] = f[i].min(f[j] + t);
15+
}
16+
}
17+
18+
f[n]
19+
}
20+
21+
fn calc(word1: &[u8], word2: &[u8], l: usize, r: usize, rev: bool) -> i32 {
22+
let mut cnt = [[0i32; 26]; 26];
23+
let mut res = 0;
24+
25+
for i in l..=r {
26+
let j = if rev { r - (i - l) } else { i };
27+
let a = (word1[j] - b'a') as usize;
28+
let b = (word2[i] - b'a') as usize;
29+
30+
if a != b {
31+
if cnt[b][a] > 0 {
32+
cnt[b][a] -= 1;
33+
} else {
34+
cnt[a][b] += 1;
35+
res += 1;
36+
}
37+
}
38+
}
39+
40+
res
41+
}
42+
}

0 commit comments

Comments
 (0)