一、介绍
1.题目描述
题目链接:https://leetcode-cn.com/problems/decode-string/
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string]
,表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a
或 2[4]
的输入。
2.测试样例
"3[a]2[bc]"
# "aaabcbc"
"3[a2[c]]"
# "accaccacc"
"2[abc]3[cd]ef"
# "abcabccdcdcdef"
"abc3[cd]xyz"
# "abccdcdcdxyz"
"3[z]2[2[y]pq4[2[jk]e1[f]]]ef"
# "zzzyypqjkjkefjkjkefjkjkefjkjkefyypqjkjkefjkjkefjkjkefjkjkefef"
二、题解
1、字符栈🟡
在这道题中,涉及到括号匹配,想到栈,定义两个栈,一个n_stk记录数字,一个c_stk记录字符串。
- 对于数字:由于数字可能>10,因此遇到数字要*10+x储存,当遇到左括号后推入栈并重置为0
- 对于左括号:遇到左括号,将当前数字和答案推入栈,并分别重置。因为遇到左括号说明后面有新的重复字符串,因此将前面的字符串暂存进栈
- 对于字符:加入到当前答案中
- 对于右括号:取出前面的字符串,将当前字符串循环加入到答案中,循环次数为n_stk的栈顶
以3[a2[c]]为例。num=0,ans=""
- 遇到3,num=3
- 遇到左括号,数字3入栈,重置num=0。ans"“入栈,重置ans=”"
- 遇到a,ans=‘a’
- 遇到2,num=2
- 遇到左括号,数字2入栈,重置num=0。ans"a"入栈,重置ans=""
- 遇到c,ans=‘c’
- 遇到右括号,取出字符串栈栈顶,赋给ans_temp=“a”;取出数字栈栈顶,赋给k=2;循环k 次,在ans_temp后面加入k个ans。完成后更新ans为ans_temp,即ans=acc
- 遇到右括号,取出字符串栈栈顶,赋给ans_temp="";取出数字栈栈顶,赋给k=3;循环k 次,在ans_temp后面加入k个ans。完成后更新ans为ans_temp,即ans=accaccacc
class Solution {
public:
string decodeString(string s) {
// 数字栈和字符栈
stack<int> n_stk;
stack<string> c_stk;
int num=0,i=0,k;
int n=s.length();
string ans="",ans_temp;
while(i<n){
// 重复次数
if(s[i]>='0'&&s[i]<='9')num=num*10+(s[i]-'0');
// 字符加入当前ans
else if(s[i]>='a'&&s[i]<='z'){
ans=ans+s[i];
}
// 遇左括号两边入栈,两边重置
else if(s[i]=='['){
n_stk.push(num);
num=0;
c_stk.push(ans);
ans="";
}
// 遇右括号循环将当前字符串加入到前面的字符串
else{
ans_temp=c_stk.top();k=n_stk.top();
while(k--) ans_temp=ans_temp+ans;
ans=ans_temp;
c_stk.pop();n_stk.pop();
}
i++;
}
return ans;
}
};
2、字符串栈🟡
除了处理字符,我们还可以将栈定义为存入字符串,其中,将单个字符s[i]转为字符串的方式为
string ss(1,s[i])
- 若为左括号或字母,压入字符串栈c_stk
- 若为右括号,弹出左括号前的所有字符,按序组合,重复k次,
- 若为数字,压入数字栈n_stk
以e3[a2[c]]为例
- 遇e,压入c_stk
- 遇3,压入n_stk
- 遇[,压入c_stk
- 遇2,压入n_stk
- 遇[,压入c_stk
- 遇c,压入n_stk
- 遇],弹出 c,重复2次,得cc。将cc压入c_stk
- 遇],弹出并组合 cc、a,得到acc,重复3次,得accaccacc,将其压入c_stk
- 遍历结束,弹出剩余元素并组合,得到eaccaccacc
class Solution {
public:
string decodeString(string s) {
stack<int> n_stk;
stack<string> c_stk;
int n=s.length();
int i=0,k;
string ans="",t="",temp="";
while(i<n){
string ss(1,s[i]);
// 左括号或字母入栈
if(ss=="["||(ss>="a"&&ss<="z")){
c_stk.push(ss);
}
// 右括号
else if(ss=="]"){
// 弹出左括号前的所有字符并按序组合
while(c_stk.top()!="["){
temp=c_stk.top();
c_stk.pop();
t=temp+t;
}
temp=t;
// 弹出左括号
c_stk.pop();
// 该串重复k次
int k=n_stk.top();
n_stk.pop();
for(int i=1;i<k;i++) t=t+temp;
// 处理完入栈
c_stk.push(t);
t="";
}
// 若为数字
else{
n_stk.push(getnum(s,i));
}
i++;
}
// 组合整个字符串
while(!c_stk.empty()){
temp=c_stk.top();
c_stk.pop();
t=temp+t;
}
return t;
}
// 次数计算,可能有多位
int getnum(string s,int &x){
int ans=0;
while(s[x]>='0'&&s[x]<='9'){
ans=ans*10+(s[x]-'0');
x++;
}
x--;
return ans;
}
};
3、递归🟡
对于该问题,每当遇到左括号,剩余的内容可以当做一个新的字符串处理。因此想到递归算法。
- 对于数字:由于数字可能>10,因此遇到数字要*10+x储存
- 对于左括号:遇到左括号,右移一个字符,递归处理左括号后面的字符
- 对于字符:加入到当前答案中
- 对于右括号:返回当前答案
以3[a2[c]]为例。第一层num1=0,第一层ans1=""
- 遇到3,第一层num1=3
- 遇到左括号,指针i右移后调用递归,即接下来处理a2[c]]
- 遇到a,第二层ans2=“a”
- 遇到2,第二层num2=2
- 遇到左括号,指针i右移后调用递归,即接下来处理c]]
- 遇到c,第三层ans3=“c”
- 遇到右括号,返回第三层ans3"c"为第二层的temp。在第二层的ans2后面加上num2个temp。即得到返回的temp=“c”,原ans2=“a”,加上2个temp后 ans2=“acc”。
- 遇到右括号,返回第二层ans2"acc"为第一层的temp。在第一层的ans1后面加上num1个temp。即得到返回的temp=“acc”,原ans1="",加上3个temp后 ans1=“accaccacc”。
- 循环结束,返回ans1
class Solution {
public:
string src;
int n,i;
string getString() {
// 仅代表该层的ans和num
string ans="";
int num=0;
while(i<n){
// 记录数字
if(src[i]>='0'&&src[i]<='9') num=num*10+(src[i]-'0');
else if(src[i]=='['){
i++;
// 进入下一层递归
string temp=getString();
// while(num--) ans=ans+temp;
for(int j=0;j<num;j++) ans=ans+temp;
num=0;
}
// 返回
else if(src[i]==']') return ans;
else ans+=src[i];
i++;
}
return ans;
}
string decodeString(string s) {
src = s;
n=src.length();
i = 0;
return getString();
}
};