Medium
题目描述
给定一个由小写英文字母组成的字符串 s。
你必须重复执行以下操作,直到字符串 s 中不再有至少两个连续字符:
- 删除字符串中最左边的一对相邻字符,这两个字符在字母表中是连续的,可以是任意顺序(例如,‘a’ 和 ‘b’,或 ‘b’ 和 ‘a’)。
- 将剩余字符向左移动以填补空隙。
返回无法执行更多操作后得到的字符串。
注意: 将字母表视为环形,因此 ‘a’ 和 ‘z’ 是连续的。
示例 1:
输入:s = "abc"
输出:"c"
解释:
从字符串中删除 "ab",剩余字符串为 "c"。
无法执行更多操作。因此,所有可能删除后的结果字符串是 "c"。
示例 2:
输入:s = "adcb"
输出:""
解释:
从字符串中删除 "dc",剩余字符串为 "ab"。
从字符串中删除 "ab",剩余字符串为 ""。
无法执行更多操作。因此,所有可能删除后的结果字符串是 ""。
示例 3:
输入:s = "zadb"
输出:"db"
解释:
从字符串中删除 "za",剩余字符串为 "db"。
无法执行更多操作。因此,所有可能删除后的结果字符串是 "db"。
约束条件:
1 <= s.length <= 10^5s仅由小写英文字母组成。
解题思路
这道题的关键是要理解删除操作的规则:删除相邻的两个字符,它们在字母表中必须是连续的(考虑环形)。
解题思路
使用栈来模拟删除过程是最直观的方法:
栈的使用:从左到右遍历字符串,使用栈来维护当前剩余的字符。
判断连续性:对于当前字符
c和栈顶字符top,判断它们是否在字母表中连续:- 正常连续:
|c - top| == 1 - 环形连续:一个是 ‘a’ 另一个是 ‘z’(即
|c - top| == 25)
- 正常连续:
操作逻辑:
- 如果栈为空,直接将当前字符入栈
- 如果当前字符与栈顶字符连续,弹出栈顶(相当于删除这对字符)
- 否则,将当前字符入栈
结果构造:遍历完成后,栈中剩余的字符就是最终结果。
这种方法能够正确处理删除操作的"最左边优先"规则,因为我们是从左到右处理的,每次遇到可以删除的情况就立即删除。
时间复杂度为 O(n),每个字符最多入栈出栈一次。空间复杂度为 O(n),栈的最大容量。
代码实现
class Solution {
public:
string resultingString(string s) {
string stack;
for (char c : s) {
if (!stack.empty() && isConsecutive(stack.back(), c)) {
stack.pop_back();
} else {
stack.push_back(c);
}
}
return stack;
}
private:
bool isConsecutive(char a, char b) {
int diff = abs(a - b);
return diff == 1 || diff == 25;
}
};
class Solution:
def resultingString(self, s: str) -> str:
stack = []
for c in s:
if stack and self.is_consecutive(stack[-1], c):
stack.pop()
else:
stack.append(c)
return ''.join(stack)
def is_consecutive(self, a: str, b: str) -> bool:
diff = abs(ord(a) - ord(b))
return diff == 1 or diff == 25
public class Solution {
public string ResultingString(string s) {
var stack = new List<char>();
foreach (char c in s) {
if (stack.Count > 0 && IsConsecutive(stack[stack.Count - 1], c)) {
stack.RemoveAt(stack.Count - 1);
} else {
stack.Add(c);
}
}
return new string(stack.ToArray());
}
private bool IsConsecutive(char a, char b) {
int diff = Math.Abs(a - b);
return diff == 1 || diff == 25;
}
}
var resultingString = function(s) {
let stack = [];
for (let char of s) {
if (stack.length > 0) {
let top = stack[stack.length - 1];
let diff = Math.abs(char.charCodeAt(0) - top.charCodeAt(0));
if (diff === 1 || diff === 25) {
stack.pop();
} else {
stack.push(char);
}
} else {
stack.push(char);
}
}
return stack.join('');
};
复杂度分析
| 复杂度类型 | 大小 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 每个字符最多入栈出栈一次,n 为字符串长度 |
| 空间复杂度 | O(n) | 栈的空间开销,最坏情况下存储所有字符 |