欢迎来到小林的博客!!
️博客主页:✈️林 子
️博客专栏:✈️ 小林的算法训练笔记
️社区 :✈️ 进步学堂
️欢迎关注:点赞收藏✍️留言
题目链接:1047. 删除字符串中的所有相邻重复项
题目描述:
给出由小写字母组成的字符串 S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
提示:
1 <= S.length <= 20000
S
仅由小写英文字母组成。解题思路:
我们只需要把字符入栈,每次入栈前和栈顶元素进行比较,如果字符相同则把栈顶POP掉,并且此次元素也不Push,这样即完成一次删除操作。反之则把元素push入栈。 因为返回值是一个字符串,所以我们可以拿字符串来模拟栈,尾删对应的是出栈,尾插对应的是入栈。
动图详解:
代码:
class Solution {
public:
string removeDuplicates(string s) {
string st;//用字符串模拟栈
for(auto& ch : s)
{
if(st.size() > 0 && st.back() == ch)
{
st.pop_back();
}else
{
st.push_back(ch);
}
}
return st;
}
};
运行结果:
题目链接:844. 比较含退格的字符串
题目描述:
给定 s
和 t
两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true
。#
代表退格字符。
**注意:**如果对空文本输入退格字符,文本继续为空。
示例 1:
输入:s = "ab#c", t = "ad#c"
输出:true
解释:s 和 t 都会变成 "ac"。
示例 2:
输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 ""。
示例 3:
输入:s = "a#c", t = "b"
输出:false
解释:s 会变成 "c",但 t 仍然是 "b"。
提示:
1 <= s.length, t.length <= 200
s
和 t
只含有小写字母以及字符 '#'
解题思路:
和上一题是一样的,只不过这次我们是遇到#则删除前一个。而上一题是当前字符和栈顶字符相等则删除栈顶字符,这个则是遇到#删除栈顶字符。然后两个字符串都做一次上面那道题的操作,然后比较一小是否相等即可,不过这里也有一个小细节,那就是#号可以出现在第一个字符,这里需要特殊判断一下。因为步骤和上面几乎一模一样,所以动图就不画了,搞懂了上一题这一题自然也能秒懂。
代码:
class Solution {
public:
bool backspaceCompare(string s, string t) {
string st1; //存储s
string st2; //存储t
for(auto& ch : s)
{
if(st1.size() > 0 && ch == '#') st1.pop_back();
else if(ch != '#') st1.push_back(ch);
}
//和上面操作一模一样
for(auto& ch : t)
{
if(st2.size() > 0 && ch == '#') st2.pop_back();
else if(ch != '#')st2.push_back(ch); //#符号不能入栈
}
return st1 == st2; //相等返回true
}
};
运行结果:
题目链接:227. 基本计算器 II
题目描述:
给你一个字符串表达式 s
,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1]
的范围内。
**注意:**不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval()
。
示例 1:
输入:s = "3+2*2"
输出:7
示例 2:
输入:s = " 3/2 "
输出:1
示例 3:
输入:s = " 3+5 / 2 "
输出:5
提示:
1 <= s.length <= 3 * 105
s
由整数和算符 ('+', '-', '*', '/')
组成,中间由一些空格隔开s
表示一个 有效表达式[0, 231 - 1]
内解题思路
因为这运算只有 +,-,*,/四则运算。所以我们只需要用一个栈来存储数据,再用一个char op变量来存储符号。当走到数字的时候遇到 X 或者 / 号。那么就从栈中pop出2个数据。进行运算后再重新放回队列,如果记录的是-号。那么我们往队列push的值需要 乘上一个-1。到最后再把栈里的值全部相加起来即可。这里需要注意一个小细节,就是我们的op需要初始化成’+'号。因为只有op为’+‘ ,我们才会把数据push进栈中。
比如下面有一个例子,3+2*2-5 ,我们来动图模拟一下。
还有一点要注意的是,leetcode给的数据中是有可能会包含空格,所以遇到空格我们要跳过。
代码:
class Solution {
public:
int calculate(string s) {
char op = '+';
stack<int> st;
int n = s.size(), i = 0;
while(i < n)
{
if(s[i] == ' ') i++; //跳过空格
else if(s[i] >= '0' && s[i] <= '9')
{
//遇到数据了,数据可能是多位数,所以我们要全部截取
int val = 0;
while(s[i] >= '0' && s[i] <= '9') val = val * 10 + (s[i++] - 48);
//如果op是+号,我们要入栈,如果是-号,我们需要 * -1 再入栈,如果是 * 和 / 先出栈,再val和出栈的数进行运算
if(op == '+')
st.push(val);
else if(op == '-')
st.push(val* -1);
else
{
//是 * 和 / ,我们把栈顶元素拿出来运算,运算完再push到栈中
int top = st.top();
st.pop();
switch(op)
{
case '*' : st.push(top * val); break;
case '/' : st.push(top/val); break;
}
}
}else
{
//遇到符号了,直接替换符号
op = s[i++];
}
}
//把栈中的元素全部取出来相加
int sum = 0 ;
while(!st.empty())
{
sum += st.top();
st.pop();
}
return sum;
}
};
运行结果:
题目链接:字符串解码
题目描述:
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string]
,表示其中方括号内部的 encoded_string
正好重复 k
次。注意 k
保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k
,例如不会出现像 3a
或 2[4]
的输入。
示例 1:
输入:s = "3[a]2[bc]"
输出:"aaabcbc"
示例 2:
输入:s = "3[a2[c]]"
输出:"accaccacc"
示例 3:
输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"
示例 4:
输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"
提示:
1 <= s.length <= 30
s
由小写英文字母、数字和方括号 '[]'
组成s
保证是一个 有效 的输入。s
中所有整数的取值范围为 [1, 300]
解题思路:
这一题我们依旧要用栈来解决,我们需要用2个栈,一个栈用来存放数字,一个栈用来存放字符串。当遇到数字时,提取数字并入数字栈。当遇到[号时,提取字符串并入字符栈。当遇到]时,也就是解码。我们需要从字符栈种拿字符,从数字栈种拿数字。随机拼接成一个新字符串,把新字符串接入到上一个栈顶的后面。我们分四种情况讨论。
情况1:遇到数字
遇到数字,那么数字可能也是多个字符串组成,所以我们需要把数字提取出来,放入 nums栈中。
情况2:遇到 [
遇到[说明要开始提取字符了,那么从当前下标的下一个位置开始提取字符串。提取完字符串后入到 strs栈中。
情况3:遇到 ]
遇到 [ 说明我们要开始解密了,我们分别提取strs栈和nums栈中的栈顶。获得一个数字k和一个字符串str。我们循环k次,每次循环都加上一个str。最后需要把strs接到栈顶的尾部,因为我们不确定前面是否还有字符。
情况4:遇到字符
遇到字符说明数字只有1,所以我们只需要把字符提取出来,接入到strs栈顶的尾部即可。
需要注意一些小细节,那就是当strs栈为空的时候,我们是无法把字符串接入到栈顶的。所以我们一开始需要push一个空串进strs栈。这样后面的字符串往空串后面拼接,还是原来的样子。
动图演示:
动图中没有显示空串,其实最底下是有个空串的。否则后面的cacacacacaca接入到栈顶元素尾部是会报错的。
代码:
class Solution {
public:
string decodeString(string s) {
stack<int> nums;
stack<string> strs;
strs.push(""); //先让空串入栈
int i = 0 , n = s.size();
while(i < n)
{
//第一种情况,如果遇到数字
if(s[i] >= '0' && s[i] <= '9')
{
int val = 0;
//拼接所有数字
while(s[i] >= '0' && s[i] <= '9') val = val * 10 + (s[i++] - 48);
//拼接完数字入栈
nums.push(val);
}
else if(s[i] == '[') //第二章情况,遇到 [
{
//从i的下一个位置开始提取字符
string val;
i++;
while(s[i] >= 'a' && s[i] <= 'z')val += s[i++];
//提取的字符入栈
strs.push(val);
}else if(s[i] == ']') // 第三种情况,遇到 ]
{
//分别从nums和strs中提取栈顶元素
int k = nums.top();
nums.pop();
string tmp = strs.top();
strs.pop();
//将 k 次 tmp字符串接入到strs栈顶的尾部
while(k--)
{
strs.top() += tmp;
}
i++; //跳到下一个位置
}else{
//第四种情况,遇到字符
//直接提取字符,接入到栈顶尾部
string val;
while(i < n && s[i] >= 'a' && s[i] <= 'z')val += s[i++];
//接入到栈顶尾部
strs.top() += val;
}
}
return strs.top(); //返回栈顶元素
}
};
运行结果:
题目链接: 946. 验证栈序列
题目描述:
给定 pushed
和 popped
两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true
;否则,返回 false
。
示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:
输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。
提示:
1 <= pushed.length <= 1000
0 <= pushed[i] <= 1000
pushed
的所有元素 互不相同popped.length == pushed.length
popped
是 pushed
的一个排列解题思路:
我们可以用一个指针指向popped数组,然后疯狂让pushed数组的值入栈。然后监测栈顶元素是否等于当前指向的popped元素。如果等于则说明出栈,一直出栈到栈顶元素不等于popped为止。最后如果栈的元素不为空,说明出栈顺序是错误的。反之则是正确的。
动图演示:
如果循环结束,栈为空,则说明poped的出栈顺序是正确的。反之则说明是错误的。
代码:
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> st;
for(int i = 0 , j = 0 ; i < pushed.size() ; i++)
{
st.push(pushed[i]);
while(!st.empty() && st.top() == popped[j])
{
st.pop();
j++;
}
}
return st.empty();
}
};
运行结果: