今天做笔试时遇到这道题,然后耗费了大半个小时也没能AC这道题,事后在力扣上找到了原题。
笔试时候的思路是和代码随想录中寻找有效括号那道题比较相似,考虑遍历时用栈进行存储字符,在遇到右括号时进行一次结果字符串的存储。也有考虑使用哈希表来存储下标和左右括号的位置以求得中间字符,再根据前面的数字来进行一次结果字符的存储。但无一例外在涉及到括号嵌套时都出了问题。
最终根据题解做出了如下C++的做法:
核心思路如下:
1.用键值对的栈来进行存储,其中键为循环的次数,值为左括号之前的字符串。用一个整型变量来存储数字,表示循环的次数,用一个字符串存储左括号之前的字符串,并作为最终的结果字符串。
2.在遇到数字时用num进行存储,因为可能连续几个字符均是数字所以需要有一个进位操作。
3.在遇到字母时将其存储到res中
4.在遇到左括号时,将目前num存储的循环次数和res存储的字符串作为一个键值对压入栈中
5.在遇到右括号时,将栈顶的键值对弹出,此时res存储的应当是括号内的字符串,count存储的是左括号前的数字,代表循环的次数,tmp代表的是左括号之前的字符串。要将括号内的字符串res进行count次重复存储到tmp之后,然后再将tmp的字符串存储到res中作为最终结果的一部分。
e.g. 例如3[a2[c]]中,在遇到第一个右括号时,此时栈内有两层,顶层为(2,"a"),底层为(3,""),此时res = "c",取出栈顶键值对进行一次循环后最终res = "acc",相当于将原本的字符串转化为了3[acc],此时再以此类推进行一次操作即可。
class Solution {
public:
string decodeString(string s) {
stack<pair<int,string>> st;//用栈存储键值对,其中键为循环的次数数字,值为存储的字符串(左括号之前的字符串)
int num = 0;//遍历过程中遇到的数字,看作之后循环的次数
string res = "";//存储最终结果,中途存储遍历到的字母
for(int i = 0; i< s.size(); i++){
if(s[i] >= '0' && s[i] <= '9'){
num *= 10;//如果连续两个字符是数字,需要进位
num += s[i] - '0';
}
else if(s[i] == '['){
//遭遇左括号,将当前存储的循环次数数字和字符串入栈,存储后需要重置
st.push(make_pair(num,res));
num = 0;
res = "";
}
else if(s[i] == ']'){
//遭遇右括号,进行出栈
//此时若存在括号嵌套现象,栈顶的键值对中存储的字符应当就是嵌套的子括号的左括号之前的字符
//值即是左括号之前的数字,代表当前的循环次数
int count = st.top().first;
string tmp = st.top().second;
st.pop();
for(int i = 0; i < count; i++) tmp = tmp + res;
res = tmp;
}
else{
//遭遇字母,进行存储
res += s[i];
}
}
return res;
}
};
启发:
1.栈也同样支持以键值对的形式进行存储。
2.本题中涉及字符串的匹配相关,应当想到用栈或队列来解决匹配问题。