----把栈理解成左端是闭合得右端开口得容器,模拟数字整体在里面得分布情况!!
这道题类似的有 Leetcode394:字符串解码,该题的求解放在博客后面。
题目:
给出一个字符串 s(仅含有小写英文字母和括号)。
请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。
注意,您的结果中 不应 包含任何括号。
示例:
输入:s = "(u(love)i)"
输出:"iloveu"
思路1:
考虑用栈的思想,因为是从内到外反转,所以就每次遇到“)”再处理,因为先遇到的右括号意味着第一批需要处理的元素出现了。除此之外其他的元素我们都给他入栈。先遇到右括号,那就出栈变成字符串,一直到遇到左括号我们不要了,然后记得把左括号也出栈,这样就实现了第一批次的反转。但是注意到反转后的元素要再次进入栈来操作啊~ 所以按顺序入栈,别慌,我们输入的是相反的,反正到时候先入后出别管,倒序不就行了~~。
class Solution {
public:
string reverseParentheses(string s) {
int n = s.length();
stack<char> stk;
for(int i = 0; i < n ; ++i) {
if(s[i] != ')') {
stk.push(s[i]);
}else {
string temp;
while(stk.top() != '(') {
temp += stk.top();
stk.pop();
}
stk.pop();
for(int j = 0; j < temp.size() ; ++j) {
stk.push(temp[j]);
}
}
}
int m = stk.size();
string res(m, 0);
for(int i = m - 1; i >= 0 ; --i) {
res[i] = stk.top(); //从栈里面再反转
stk.pop();
}
return res;
}
};
类似的,不用栈,用数组来操作,思路一致:
class Solution {
public:
string reverseParentheses(string s) {
int n = s.size();
string res;
//统计当前左括号数目, 然后遇到右括号就弹出
for(int i = 0;i < n;i++)
{
if(s[i] == ')')
{
string temp;
while(res.back() != '(')
{
temp += res.back();
res.pop_back();
}
res.pop_back(); //去掉括号
res += temp;
// cout<<res<<endl;
}
else res += s[i];
}
return res;
}
};
思路2:
Leetcode官方题解的第一种方法。考虑到括号是从中心到外部对称的。所以对左括号而言,遇到左括号意味着某一层的终止,将前面记录的字符串推入栈中,作为某一层字符串,然后字符串清零,继续记录。也就是说右括号的出现一定是所有左括号遍历结束之后产生的。那么当遇到右括号之后就开始操作了,第一个右括号代表最中心的一层,对他进行反转,然后放到上一层的字符尾巴,然后有新字符的话进行加入字符,这里字符就不清零了,而是每次遇到一个右括号意味着到了一层,和之前栈里面提取的元素进行大反转,然后再提取元素准备一下次。注意到刚开始stk加入了个空元素。所以最后我再准备个空元素没毛病。
class Solution {
public:
string reverseParentheses(string s) {
stack<string> stk;
string str;
for (auto &ch : s) {
if (ch == '(') {
stk.push(str); //左括号之前存的是一层一层的 str字符串而不是字符
str = "";
} else if (ch == ')') {
reverse(str.begin(), str.end());
str = stk.top() + str;
stk.pop();
} else {
str.push_back(ch);
}
}
return str;
}
};
————————————————————————————————————————
Leetcode394:字符串解码
题目:
1.给定一个经过编码的字符串,返回它解码后的字符串。
2.编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
3.你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
4.此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
思路1:
数字存放在数字栈,字符串和左括号存放在字符串栈,遇到右括号时候弹出一个数字栈,字母栈弹到左括号为止,然后删除左括号。 数字和字母乘法后反转返回原来字符栈(第一次实现,然后作为下一次的实现子部分),为什么要反转呢,这是因为我一开始入栈是正常顺序。现在复制了也是正常顺序入栈。最后整体反转就好了。不然因为是多层的。每次都是反的负负得正了要麻烦~
注意到,这个方法中输入的是char,每一个char都单独进入char栈中~ 所以效率相对低一点。考虑到是否可以用string的方式一层层输入呢?请看思路2。
//数字存放在数字栈,字符串存放在字符串栈,遇到右括号时候弹出一个数字栈,字母栈弹到左括号为止。
class Solution {
public:
string decodeString(string s) {
string res = "";
stack <int> nums; //建立数字栈
stack <char> strs; //建立字符栈 但是注意到我们输入的是char所以就是char栈
int num = 0;
int len = s.size();
for(int i=0; i<len; i++){
if(s[i]>='0' && s[i]<='9')
num=num*10+s[i]-'0'; //-'0'还是-"0"看情况把
if(s[i]=='['){
nums.push(num);
num=0;
strs.push(s[i]);
}
if(s[i]>='a' && s[i]<='z' || s[i]>='A' && s[i]<='Z'){
strs.push(s[i]);
}
if(s[i]==']'){
int count=nums.top();
nums.pop();
string stemp;
while(strs.top()!='['){
stemp+=strs.top();
strs.pop();
}
reverse(stemp.begin(), stemp.end());
strs.pop();
string newstemp;
for(int i=1;i<=count;i++){
newstemp=newstemp+stemp;
}
for(int i=0;i<newstemp.size();i++)
strs.push(newstemp[i]);
}
}
int m=strs.size();
for(int i=0; i<m; i++){
res.push_back(strs.top());
strs.pop();
}
reverse(res.begin(), res.end());
return res;
}
};
思路2: 碰到’[’: 数字和当前字符串入栈. 碰到 ‘]’: 数字和字符串出栈.每一次左括号会把之前积累的字符串进栈,同时完成数字记录。每次右括号当前操作的字符串没来得及进栈,那就是根据栈头就是之前积累元素的原则,根据当前的num值不断更新栈头的字符完成后。之前的元素在栈就直接丢出来。然后res就是截至当前积累的值。如果后面还是字母那就加上,如果遇到左括号说明遇到不同层得了,那就入栈。牢记,每次左括号的出现,代表进入了新的一层,**我们的原则是遇到右括号才处理,所以原则其实是从内往外操作。**因为当内嵌结构首次出现右括号,就是最里面一层。考虑到当前层出来可能还在上一层结构内,那就结合~
class Solution {
public:
string decodeString(string s) {
string res = "";
stack <int> nums;
stack <string> strs;
int num = 0;
int len = s.size();
for(int i = 0; i < len; ++ i)
{
if(s[i] >= '0' && s[i] <= '9')
{
num = num * 10 + s[i] - '0';
}
else if((s[i] >= 'a' && s[i] <= 'z') ||(s[i] >= 'A' && s[i] <= 'Z'))
{
res = res + s[i];
}
else if(s[i] == '[') //将‘[’前的数字压入nums栈内, 字母字符串压入strs栈内
{
nums.push(num);
num = 0;
strs.push(res);
res = "";
}
else //遇到‘]’时,操作与之相配的‘[’之间的字符
{
int times = nums.top();
nums.pop();
for(int j = 0; j < times; ++ j)
strs.top() += res;
res = strs.top(); //之后若还是字母,就会直接加到res之后,因为它们是同一级的运算
//若是左括号,res会被压入strs栈,作为上一层的运算
strs.pop(); //strs.top()里面的东西已经没用了 在我的res里面。
}
}
return res;
}
};
//在这里,左括号用来终结上一次的。 每一次遇到右括号就对上一层的和当前层的元素进行操作,因为当前元素没有入栈,没事直接和上一层的top叠加入栈,然后赋值给res 出栈~~~ 完美~