leetcode:394. 字符串解码
题目
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
示例:
输入:s = "3[a]2[bc]"输出:"aaabcbc"
输入:s = "3[a2[c]]"输出:"accaccacc"
输入:s = "2[abc]3[cd]ef"输出:"abcabccdcdcdef"
输入:s = "abc3[cd]xyz"输出:"abccdcdcdxyz"
解答 & 代码
解法一:模拟 + 递归
class Solution {private:// 将字符串 s 重复 k 次并返回string repeatStringKTimes(string s, int k){string result = "";for(int i = 0; i < k; ++i)result += s;return result;}public:string decodeString(string s) {string result = ""; // 结果int idx = 0; // 当前下标// 遍历字符串 swhile(idx < s.size()){// 1. 处理字母while(idx < s.size() && (s[idx] >= 'a' && s[idx] <= 'z')){result += s[idx];++idx;}// 2. 处理 k[encoded_string],括号里面可能存在嵌套括号// 2.1 处理数字 kint number = 0; // 重复次数 kwhile(idx < s.size() && (s[idx] >= '0' && s[idx] <= '9')){number = number * 10 + s[idx] - '0';++idx;}// 2.2 跳过 "["if(s[idx] == '[')++idx;// 2.3 处理括号中的 encoded_stringstring curStr = ""; // 括号中的 encoded_string,即待被重复的字符串while(idx < s.size() && s[idx] != ']'){// 如果是字母,直接加到 curStrif(idx < s.size() && s[idx] >= 'a' && s[idx] <= 'z')curStr += s[idx];// 如果是数字,则提取内容 k_2[encoded_string_2],递归处理else if(s[idx] >= '0' && s[idx] <= '9'){string subS = "";int leftBracket = 0; // 左括号数目int rightBracket = 0; // 右括号数目while(idx < s.size()){char ch = s[idx];++idx;subS += ch;if(s[idx] == '[')++leftBracket;else if(s[idx] == ']'){++rightBracket;// 若遍历到右括号,且遍历到的右括号数 == 左括号数,则退出当前循环if(leftBracket == rightBracket)break;};}// 递归解码 k_2[encoded_string_2],并加到 curStr 后面curStr += decodeString(subS);}++idx;}// 2.4 跳过 "]"if(s[idx] == ']')++idx;// 2.5 将当前的 curStr 重复 number 次result += repeatStringKTimes(curStr, number);}return result;}};
复杂度分析:设字符串 s 长为 N
- 时间复杂度 O(N):
- 空间复杂度 O(N):取决于递归深度,即字符串
s中括号套括号的最大深度,最坏情况下为 O(N),eg.2[2[2[a]]]
执行结果:
执行结果:通过执行用时:0 ms, 在所有 C++ 提交中击败了 100.00% 的用户内存消耗:6.2 MB, 在所有 C++ 提交中击败了 93.27% 的用户
解法二:辅助栈法
本题难点在于括号内嵌套括号,需要从内向外生成与拼接字符串,这与栈的先入后出特性对应。
核心思路就是用两个栈分别保存字符串和数字(重复次数),如果遇到 "[" 则入栈,遇到 "]" 则出栈
class Solution {public:string decodeString(string s) {// 辅助栈stack<string> resultStack; // 存储字符串stack<int> numberStack; // 存储数字(重复次数)string result = ""; // 结果字符串int number = 0; // 数字(重复次数)// 遍历字符串 s 中的每个字符 s[i]for(int i = 0; i < s.size(); ++i){char ch = s[i];// case 1. 若当前字符为字母,则添加到 result 尾部if(ch >= 'a' && ch <= 'z')result += ch;// case 2. 若当前字符为数字,则更新 numberelse if(ch >= '0' && ch <= '9')number = number * 10 + ch - '0';// case 3. 若当前是左括号,则将当前的 result 和 number 压入栈中,并重新初始化else if(ch == '['){resultStack.push(result); // 将当前的 result 压入栈numberStack.push(number); // 将当前的 number 压入栈result = ""; // 将 result 清空number = 0; // 将 number 清零}// case 4. 若当前是右括号,则从栈中弹出之前的 result 和 number,来拼接字符串else if(ch == ']'){string lastResult = resultStack.top(); // 之前的字符串resultStack.pop();int lastNumber = numberStack.top(); // 代表当前字符串需要重复的次数numberStack.pop();// 将当前字符串重复 lastNumber 次,拼接到之前的字符串之后for(int j = 0; j < lastNumber; ++j)lastResult += result;result = lastResult; // 更新当前字符串}}return result;}};
复杂度分析:设字符串 s 长为 N
- 时间复杂度 O(N):一次遍历
s - 空间复杂度 O(N):辅助栈大小取决于字符串
s中括号套括号的最大深度,最坏情况下为 O(N),eg.2[2[2[a]]]
执行结果:通过执行用时:0 ms, 在所有 C++ 提交中击败了 100.00% 的用户内存消耗:6.4 MB, 在所有 C++ 提交中击败了 45.55% 的用户
解法三:递归法
class Solution {private:int end = 0; // dfs 递归函数返回时遍历到的下标// 递归函数定义:从下标 start 开始解码字符串 s,并返回解码后的字符串,返回时更新索引 endstring dfs(string s, int start){string result = ""; // 解码后的结果字符串int number = 0; // 代表要重复的次数int idx = start; // 下标// 遍历字符串while(idx < s.size()){char ch = s[idx];// case 1:如果当前字符是字母,则添加到 result 尾部if(ch >= 'a' && ch <= 'z')result += ch;// case 2:如果当前字符是数字,则更新 numberelse if(ch >= '0' && ch <= '9')number = number * 10 + ch - '0';// case 3:如果当前字符是 "[",则开启新一层递归,并拼接else if(ch == '['){// 递归得到括号里面解码后的字符串string subStr = dfs(s, idx + 1);// 将 subStr 重复 number 次,添加到 result 尾部// 同时 number 清零while(number > 0){result += subStr;--number;}// 更新下标为上面递归函数遍历到的最后一个字符的位置idx = end;}// case 4:如果当前字符是 "]",则触发递归结束条件// 用 end 记录当前访问到的下标,并返回本次递归得到的字符串 resultelse if(ch == ']'){end = idx;return result;}// 准备遍历下一个字符++idx;}end = idx; // 这里写不写都行return result;}public:string decodeString(string s) {return dfs(s, 0);}};
复杂度分析:设字符串 s 长为 N
- 时间复杂度 O(N):递归返回的时候会更新当前已遍历到的下标 end,因此实际还是一次遍历
s - 空间复杂度 O(N):递归深度(递归栈空间复杂度)取决于字符串
s中括号套括号的最大深度,最坏情况下为 O(N),eg.2[2[2[a]]]
执行结果:通过执行用时:0 ms, 在所有 C++ 提交中击败了 100.00% 的用户内存消耗:6.2 MB, 在所有 C++ 提交中击败了95.86% 的用户
