给你一个字符串 s 。它可能包含任意数量的 ‘’ 字符。你的任务是删除所有的 '’ 字符。
当字符串还存在至少一个 ‘*’ 字符时,你可以执行以下操作:
删除最左边的 ‘’ 字符,同时删除该星号字符左边一个字典序 最小 的字符。如果有多个字典序最小的字符,你可以删除它们中的任意一个。
请你返回删除所有 '’ 字符以后,剩余字符连接而成的
字典序最小
的字符串。
示例 1:
输入:s = “aaba*”
输出:“aab”
解释:
删除 ‘*’ 号和它左边的其中一个 ‘a’ 字符。如果我们选择删除 s[3] ,s 字典序最小。
示例 2:
输入:s = “abc”
输出:“abc”
解释:
字符串中没有 ‘*’ 字符。
法一
class Solution {
public:
string clearStars(string s) {
int n = s.size();
stack<int> st[26];
for(int i = 0; i < n; i++){
if(s[i] != '*'){
st[s[i] - 'a'].push(i);
}
else{
for(auto &p : st){
if(!p.empty()){
s[p.top()] = '*';
p.pop();
break;
}
}
}
}
s.erase(remove(s.begin(),s.end(),'*'), s.end());
return s;
}
};
时间复杂度:O(n∣Σ∣),其中 n 是 s 的长度,∣Σ∣ 为字符集合的大小,本题字符均为小写字母,所以 ∣Σ∣=26。
空间复杂度:O(n+∣Σ∣)。
这道题我们首先建立一个大小为26的栈数组,每个st[i]代表一个栈。我们按顺序遍历字符串,在遍历过程中,如果字符不为*,那么就在栈数组对应的栈推入这个字符的位置。比如说字符为a在i=0的位置,那么就是st[0] = {0}。
如果字符是*,那么我们就循环栈数组,栈数组会按字典序从小到大进行查询,优先删除字典序最小的字符。我们查找到字典序最小的字符所处的栈后,栈顶的元素代表这当前最靠右的字符的位置,我们在字符串s中将这个字符 s[p.top()] = '*';
设为星号,代表这个字符已经被删除。然后执行完后,该栈就要弹出栈顶的元素的位置,因为他已经被删除不存在。一旦找到一个不为空的栈,就break,停止遍历栈数组。
最后 s.erase(remove(s.begin(), s.end(), '*'), s.end());
,举个例子:
remove 会将字符串变为 “abcdef**”,并返回一个指向第一个 ‘’ 的位置的迭代器。
erase 会删除这两个 '’,最终 s 变为 “abcdef”。
位运算优化
class Solution {
public:
string clearStars(string s) {
int n = s.size(), mask = 0;
stack<int> st[26];
for(int i = 0; i < n; i++){
if(s[i] != '*'){
st[s[i] - 'a'].push(i);
mask |= 1<<(s[i]-'a');
}
else{
int k = __builtin_ctz(mask);
auto& p = st[k];
s[p.top()] = '*';
p.pop();
if(p.empty()){
mask ^= 1 << k;
}
}
}
s.erase(remove(s.begin(),s.end(),'*'), s.end());
return s;
}
};
时间复杂度:O(n+∣Σ∣),其中 n 是 s 的长度,∣Σ∣ 为字符集合的大小,本题字符均为小写字母,所以 ∣Σ∣=26。
空间复杂度:O(n+∣Σ∣)。
位运算优化的地方在我们之前通过遍历栈数组来查找最小字典序的栈是哪个,我们可以使用一个二进制数mask通过0和1来记录每个栈是否不为空。在循环字符串s的过程中,字符不为星号,则让mask对应位置变为1,说明这个字母存在。
如果遍历s的过程中字符为星号,则定义一个整数k,调用内置函数__builtin_ctz(mask)来返回mask中最右侧的1的右边有多少个0。k就是我们需要查找的栈数组中的栈的位置。然后最后,如果栈推出元素后为空,那么就通过异或1来将mask中的1置为0,代表这个栈为空。