Skip to content

Commit 29393dc

Browse files
2xiao吴晓晓
authored andcommitted
add 2825 2337
1 parent 5995cd3 commit 29393dc

File tree

2 files changed

+377
-0
lines changed

2 files changed

+377
-0
lines changed

src/problem/2337.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
---
2+
title: 2337. 移动片段得到字符串
3+
description: LeetCode 2337. 移动片段得到字符串题解,Move Pieces to Obtain a String,包含解题思路、复杂度分析以及完整的 JavaScript 代码实现。
4+
keywords:
5+
- LeetCode
6+
- 2337. 移动片段得到字符串
7+
- 移动片段得到字符串
8+
- Move Pieces to Obtain a String
9+
- 解题思路
10+
- 双指针
11+
- 字符串
12+
---
13+
14+
# 2337. 移动片段得到字符串
15+
16+
🟠 <font color=#ffb800>Medium</font>&emsp; 🔖&ensp; [`双指针`](/tag/two-pointers.md) [`字符串`](/tag/string.md)&emsp; 🔗&ensp;[`力扣`](https://leetcode.cn/problems/move-pieces-to-obtain-a-string) [`LeetCode`](https://leetcode.com/problems/move-pieces-to-obtain-a-string)
17+
18+
## 题目
19+
20+
You are given two strings `start` and `target`, both of length `n`. Each
21+
string consists **only** of the characters `'L'`, `'R'`, and `'_'` where:
22+
23+
- The characters `'L'` and `'R'` represent pieces, where a piece `'L'` can move to the **left** only if there is a **blank** space directly to its left, and a piece `'R'` can move to the **right** only if there is a **blank** space directly to its right.
24+
- The character `'_'` represents a blank space that can be occupied by **any** of the `'L'` or `'R'` pieces.
25+
26+
Return `true` _if it is possible to obtain the string_ `target` _by moving the
27+
pieces of the string_`start` _**any** number of times_. Otherwise, return
28+
`false`.
29+
30+
**Example 1:**
31+
32+
> Input: `start = "_L__R__R_", target = "L______RR"`
33+
>
34+
> Output: true
35+
>
36+
> Explanation: We can obtain the string target from start by doing the following moves:
37+
>
38+
> - Move the first piece one step to the left, start becomes equal to `"L___R__R_"`.
39+
> - Move the last piece one step to the right, start becomes equal to `"L___R___R"`.
40+
> - Move the second piece three steps to the right, start becomes equal to `"L______RR"`.
41+
>
42+
> Since it is possible to get the string target from start, we return true.
43+
44+
**Example 2:**
45+
46+
> Input: `start = "R_L_", target = "__LR"`
47+
>
48+
> Output: false
49+
>
50+
> Explanation: The 'R' piece in the string start can move one step to the right to obtain `"_RL_"`.
51+
>
52+
> After that, no pieces can move anymore, so it is impossible to obtain the string target from start.
53+
54+
**Example 3:**
55+
56+
> Input: `start = "_R", target = "R_"`
57+
>
58+
> Output: false
59+
>
60+
> Explanation: The piece in the string start can move only to the right, so it is impossible to obtain the string target from start.
61+
62+
**Constraints:**
63+
64+
- `n == start.length == target.length`
65+
- `1 <= n <= 10^5`
66+
- `start` and `target` consist of the characters `'L'`, `'R'`, and `'_'`.
67+
68+
## 题目大意
69+
70+
给你两个字符串 `start``target` ,长度均为 `n` 。每个字符串 **** 由字符 `'L'``'R'``'_'`
71+
组成,其中:
72+
73+
- 字符 `'L'``'R'` 表示片段,其中片段 `'L'` 只有在其左侧直接存在一个 **空位** 时才能向 **** 移动,而片段 `'R'` 只有在其右侧直接存在一个 **空位** 时才能向 **** 移动。
74+
- 字符 `'_'` 表示可以被 **任意** `'L'``'R'` 片段占据的空位。
75+
76+
如果在移动字符串 `start` 中的片段任意次之后可以得到字符串 `target` ,返回 `true` ;否则,返回 `false`
77+
78+
**示例 1:**
79+
80+
> **输入:** `start = "_L__R__R_", target = "L______RR"`
81+
>
82+
> **输出:** true
83+
>
84+
> **解释:** 可以从字符串 start 获得 target ,需要进行下面的移动:
85+
>
86+
> - 将第一个片段向左移动一步,字符串现在变为 `"L___R__R_"`
87+
> - 将最后一个片段向右移动一步,字符串现在变为 `"L___R___R"`
88+
> - 将第二个片段向右移动三步,字符串现在变为 `"L______RR"`
89+
>
90+
> 可以从字符串 start 得到 target ,所以返回 true 。
91+
92+
**示例 2:**
93+
94+
> **输入:** `start = "R_L_", target = "__LR"`
95+
>
96+
> **输出:** false
97+
>
98+
> **解释:** 字符串 start 中的 'R' 片段可以向右移动一步得到 `"_RL_"`
99+
>
100+
> 但是,在这一步之后,不存在可以移动的片段,所以无法从字符串 start 得到 target 。
101+
102+
**示例 3:**
103+
104+
> **输入:** `start = "_R", target = "R_"`
105+
>
106+
> **输出:** false
107+
>
108+
> **解释:** 字符串 start 中的片段只能向右移动,所以无法从字符串 start 得到 target 。
109+
110+
**提示:**
111+
112+
- `n == start.length == target.length`
113+
- `1 <= n <= 10^5`
114+
- `start``target` 由字符 `'L'``'R'``'_'` 组成
115+
116+
## 解题思路
117+
118+
本题要求验证是否可以通过移动 `'L'``'R'` 从字符串 `start` 变为字符串 `target`,移动规则是:
119+
120+
1. `'L'` 可以向左移动,但不能向右;
121+
2. `'R'` 可以向右移动,但不能向左;
122+
3. `start``target` 的空位 `_` 可以被占据。
123+
124+
如果 `start` 移动后可以得到 `target`,必须满足以下要求:
125+
126+
- `start``target``'L'``'R'` 的相对顺序必须一致。换句话说,`start``target` 去掉 `'_'` 后必须有相同的字符序列,否则无法通过任何移动变换得到。
127+
- 对于 `'L'``start` 中的 `'L'` 的下标必须大于等于 `target` 中对应 `'L'` 的下标,因为 `'L'` 只能向左移动。
128+
- 对于 `'R'``start` 中的 `'R'` 的下标必须小于等于 `target` 中对应 `'R'` 的下标,因为 `'R'` 只能向右移动。
129+
130+
因此,可以使用两个指针分别遍历 `start``target`,逐一验证每个 `'L'``'R'` 的位置关系是否符合上述规则。
131+
132+
1.`start``target` 中去掉所有的 `'_'`,如果剩下的字符序列不同,直接返回 `false`
133+
2. 使用双指针遍历 `start``target`
134+
- 如果遇到 `'L'`,检查 `start` 中的索引是否大于等于 `target` 中的索引。
135+
- 如果遇到 `'R'`,检查 `start` 中的索引是否小于等于 `target` 中的索引。
136+
3. 如果所有的字符都满足规则,返回 `true`;否则返回 `false`
137+
138+
#### 复杂度分析
139+
140+
- **时间复杂度**`O(n)`,其中 `n` 是字符串的长度,双指针遍历字符串 `start``target`,每个字符最多遍历一次。
141+
- **空间复杂度**`O(1)`,仅使用了固定数量的变量,没有额外空间开销。
142+
143+
## 代码
144+
145+
```javascript
146+
/**
147+
* @param {string} start
148+
* @param {string} target
149+
* @return {boolean}
150+
*/
151+
var canChange = function (start, target) {
152+
let n = start.length;
153+
let i = 0,
154+
j = 0;
155+
156+
// 双指针遍历
157+
while (i < n || j < n) {
158+
// 跳过 start 和 target 中的空位 '_'
159+
while (i < n && start[i] === '_') i++;
160+
while (j < n && target[j] === '_') j++;
161+
162+
// 如果两者同时结束,说明匹配成功
163+
if (i === n && j === n) return true;
164+
165+
// 如果只有一个结束,说明匹配失败
166+
if (i === n || j === n) return false;
167+
168+
// 当前字符必须一致,否则匹配失败
169+
if (start[i] !== target[j]) return false;
170+
171+
// 验证移动规则
172+
if (start[i] === 'L' && i < j) return false; // 'L' 不能向右移动
173+
if (start[i] === 'R' && i > j) return false; // 'R' 不能向左移动
174+
175+
// 移动指针
176+
i++;
177+
j++;
178+
}
179+
180+
return true;
181+
};
182+
```
183+
184+
## 相关题目
185+
186+
<!-- prettier-ignore -->
187+
| 题号 | 标题 | 题解 | 标签 | 难度 | 力扣 |
188+
| :------: | :------ | :------: | :------ | :------: | :------: |
189+
| 20 | 有效的括号 | [[]](/problem/0020.md) | [``](/tag/stack.md) [`字符串`](/tag/string.md) | 🟢 | [🀄️](https://leetcode.cn/problems/valid-parentheses) [🔗](https://leetcode.com/problems/valid-parentheses) |
190+
| 777 | 在LR字符串中交换相邻字符 | | [`双指针`](/tag/two-pointers.md) [`字符串`](/tag/string.md) | 🟠 | [🀄️](https://leetcode.cn/problems/swap-adjacent-in-lr-string) [🔗](https://leetcode.com/problems/swap-adjacent-in-lr-string) |

src/problem/2825.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
---
2+
title: 2825. 循环增长使字符串子序列等于另一个字符串
3+
description: LeetCode 2825. 循环增长使字符串子序列等于另一个字符串题解,Make String a Subsequence Using Cyclic Increments,包含解题思路、复杂度分析以及完整的 JavaScript 代码实现。
4+
keywords:
5+
- LeetCode
6+
- 2825. 循环增长使字符串子序列等于另一个字符串
7+
- 循环增长使字符串子序列等于另一个字符串
8+
- Make String a Subsequence Using Cyclic Increments
9+
- 解题思路
10+
- 双指针
11+
- 字符串
12+
---
13+
14+
# 2825. 循环增长使字符串子序列等于另一个字符串
15+
16+
🟠 <font color=#ffb800>Medium</font>&emsp; 🔖&ensp; [`双指针`](/tag/two-pointers.md) [`字符串`](/tag/string.md)&emsp; 🔗&ensp;[`力扣`](https://leetcode.cn/problems/make-string-a-subsequence-using-cyclic-increments) [`LeetCode`](https://leetcode.com/problems/make-string-a-subsequence-using-cyclic-increments)
17+
18+
## 题目
19+
20+
You are given two **0-indexed** strings `str1` and `str2`.
21+
22+
In an operation, you select a **set** of indices in `str1`, and for each index
23+
`i` in the set, increment `str1[i]` to the next character **cyclically**. That
24+
is `'a'` becomes `'b'`, `'b'` becomes `'c'`, and so on, and `'z'` becomes
25+
`'a'`.
26+
27+
Return `true` _if it is possible to make_`str2` _a subsequence of_`str1` _by
28+
performing the operation**at most once**_ , _and_ `false` _otherwise_.
29+
30+
**Note:** A subsequence of a string is a new string that is formed from the
31+
original string by deleting some (possibly none) of the characters without
32+
disturbing the relative positions of the remaining characters.
33+
34+
**Example 1:**
35+
36+
> Input: str1 = "abc", str2 = "ad"
37+
>
38+
> Output: true
39+
>
40+
> Explanation: Select index 2 in str1.
41+
>
42+
> Increment str1[2] to become 'd'.
43+
>
44+
> Hence, str1 becomes "abd" and str2 is now a subsequence. Therefore, true is returned.
45+
46+
**Example 2:**
47+
48+
> Input: str1 = "zc", str2 = "ad"
49+
>
50+
> Output: true
51+
>
52+
> Explanation: Select indices 0 and 1 in str1.
53+
>
54+
> Increment str1[0] to become 'a'.
55+
>
56+
> Increment str1[1] to become 'd'.
57+
>
58+
> Hence, str1 becomes "ad" and str2 is now a subsequence. Therefore, true is returned.
59+
60+
**Example 3:**
61+
62+
> Input: str1 = "ab", str2 = "d"
63+
>
64+
> Output: false
65+
>
66+
> Explanation: In this example, it can be shown that it is impossible to make str2 a subsequence of str1 using the operation at most once.
67+
>
68+
> Therefore, false is returned.
69+
70+
**Constraints:**
71+
72+
- `1 <= str1.length <= 10^5`
73+
- `1 <= str2.length <= 10^5`
74+
- `str1` and `str2` consist of only lowercase English letters.
75+
76+
## 题目大意
77+
78+
给你一个下标从 **0** 开始的字符串 `str1``str2`
79+
80+
一次操作中,你选择 `str1` 中的若干下标。对于选中的每一个下标 `i` ,你将 `str1[i]` **循环** 递增,变成下一个字符。也就是说
81+
`'a'` 变成 `'b'``'b'` 变成 `'c'` ,以此类推,`'z'` 变成 `'a'`
82+
83+
如果执行以上操作 **至多一次** ,可以让 `str2` 成为 `str1` 的子序列,请你返回 `true` ,否则返回 `false`
84+
85+
**注意:** 一个字符串的子序列指的是从原字符串中删除一些(可以一个字符也不删)字符后,剩下字符按照原本先后顺序组成的新字符串。
86+
87+
**示例 1:**
88+
89+
> **输入:** str1 = "abc", str2 = "ad"
90+
>
91+
> **输出:** true
92+
>
93+
> **解释:** 选择 str1 中的下标 2 。
94+
>
95+
> 将 str1[2] 循环递增,得到 'd' 。
96+
>
97+
> 因此,str1 变成 "abd" 且 str2 现在是一个子序列。所以返回 true 。
98+
99+
**示例 2:**
100+
101+
> **输入:** str1 = "zc", str2 = "ad"
102+
>
103+
> **输出:** true
104+
>
105+
> **解释:** 选择 str1 中的下标 0 和 1 。
106+
>
107+
> 将 str1[0] 循环递增得到 'a' 。
108+
>
109+
> 将 str1[1] 循环递增得到 'd' 。
110+
>
111+
> 因此,str1 变成 "ad" 且 str2 现在是一个子序列。所以返回 true 。
112+
113+
**示例 3:**
114+
115+
> **输入:** str1 = "ab", str2 = "d"
116+
>
117+
> **输出:** false
118+
>
119+
> **解释:** 这个例子中,没法在执行一次操作的前提下,将 str2 变为 str1 的子序列。
120+
>
121+
> 所以返回 false 。
122+
123+
**提示:**
124+
125+
- `1 <= str1.length <= 10^5`
126+
- `1 <= str2.length <= 10^5`
127+
- `str1``str2` 只包含小写英文字母。
128+
129+
## 解题思路
130+
131+
**字符比较规则**
132+
133+
对于 `str1[i]``str2[j]`,如果:
134+
135+
- `str1[i] == str2[j]`,匹配成功。
136+
- `(str1[i] + 1) % 26 == str2[j]`,表示 `str1[i]` 通过一次循环递增可以变成 `str2[j]`
137+
138+
**双指针法**
139+
140+
- 指针 `prev1` 遍历 `str1`
141+
- 指针 `prev2` 遍历 `str2`
142+
- 每次当 `str1[prev1]` 满足上述匹配规则时,将 `prev2` 向前移动一位,表示 `str2[prev2]` 找到了对应的字符。
143+
- 如果最后 `prev2` 能遍历完 `str2`,则说明 `str2``str1` 的子序列。
144+
145+
**算法步骤**
146+
147+
1. 初始化指针 `prev1``prev2` 均为 0。
148+
2. 遍历 `str1`,对当前字符进行判断:
149+
- 如果 `str1[prev1]``str2[prev2]` 匹配,移动 `prev2`
150+
- 无论是否匹配,指针 `prev1` 总是向右移动。
151+
3. 检查 `prev2` 是否等于 `str2.length`,如果是,则返回 `true`;否则返回 `false`
152+
153+
#### 复杂度分析
154+
155+
- **时间复杂度**`O(n)`,其中 `n = str1.length`,每个字符最多遍历一次。
156+
- **空间复杂度**`O(1)`,使用了固定数量的变量。
157+
158+
## 代码
159+
160+
```javascript
161+
/**
162+
* @param {string} str1
163+
* @param {string} str2
164+
* @return {boolean}
165+
*/
166+
var canMakeSubsequence = function (str1, str2) {
167+
let prev1 = 0,
168+
prev2 = 0;
169+
while (prev1 < str1.length && prev2 < str2.length) {
170+
if (
171+
str1[prev1] == str2[prev2] ||
172+
(str1.charCodeAt(prev1) + 1 - str2.charCodeAt(prev2)) % 26 == 0
173+
) {
174+
prev2++;
175+
}
176+
prev1++;
177+
}
178+
return prev2 == str2.length;
179+
};
180+
```
181+
182+
## 相关题目
183+
184+
<!-- prettier-ignore -->
185+
| 题号 | 标题 | 题解 | 标签 | 难度 | 力扣 |
186+
| :------: | :------ | :------: | :------ | :------: | :------: |
187+
| 392 | 判断子序列 | [[]](/problem/0392.md) | [`双指针`](/tag/two-pointers.md) [`字符串`](/tag/string.md) [`动态规划`](/tag/dynamic-programming.md) | 🟢 | [🀄️](https://leetcode.cn/problems/is-subsequence) [🔗](https://leetcode.com/problems/is-subsequence) |

0 commit comments

Comments
 (0)