decodeString

decodeString

此题是leetCode 394

第一个版本写的非常挫折,代码也十分混乱。比较大的收获是gdb调试熟悉多了. 这也是比较挫的实现,让人失之东隅收之桑榆吧! 总结一些心得:

  • 先不要着急开始写;
  • 有些情况是递归方便, 有些情况是非递归方便(例如按层遍历树, 包括此题). 前提是, 如果需要递归把递归的操作拆解清楚(返回类型, 入参等); 对于递归情况很复杂的,尝试拆解手工栈的操作;
  • 对于这种明显利用栈的, 先把栈操作的内容搞清楚, 入栈出栈都是哪些内容? 时机是哪些? 出栈后的具体动作是什么? 此题解法2,3中是数字和字符入栈,而不是’[’, '['只是标记入栈的时机。这些问题没有搞清,就不要想着直接去走递归利用系统栈去得到解法。如果顺利可以得到,但大概率会不顺利。对于设计其他数据结构的把基本操作也先尝试搞清.
  • 有关解析状态:
    • 一般还是写一下BNF表达式比较好(而不是直接用脑子想),有助于搞清楚所处的若干个状态; 理解下一个字符对状态转换的分支的作用; 有关这点参考一下第一个递归解法的注释;
    • 先搞1个或几个具体的例子,拆解解析字符的每一步的动作应该是什么(入栈, 出栈, 具体动作, etc.)
  • 使用iterator或integer counter不是问题关键。搞清前面2项才是关键;

解法1: 最初的递归版本

#include <iostream>
#include <cctype>
#include <string>
#include <vector>

using namespace std;

class Solution {
public:
    string decodeString(const string &s);

private:
    vector<char> stack_;

    int parse_repeat_num_(string::const_iterator *beg); 
    string decodeString_(string::const_iterator *beg);
    string makeup_partial_string(int repeat_num, const string& str);
    string handleStartWithDigit(string::const_iterator *beg);
};



int Solution::parse_repeat_num_(string::const_iterator *beg) {
  string repeat_num_str = "";
  while (isdigit(**beg)) {
    repeat_num_str += **beg;
    (*beg)++;
  }      
  return stoi(repeat_num_str);
}

string Solution::decodeString(const string& s) {
  auto st = s.cbegin(), ed = s.cend();
  string ans("");

  do {
    string tmp = decodeString_(&st);
    ans += tmp;
    cout << "tmp=" << tmp << endl;
  } 
  while (st != ed);
   
  cout << "result=" << ans << endl; 
  return ans;
}

string Solution::makeup_partial_string(int repeat_num, const string& str) {
  string tmp = str;
  string result = string(str); 
  while (--repeat_num) {
    result += tmp;
  }    
  return result;
}

string Solution::handleStartWithDigit(string::const_iterator *beg) {
    auto *st = beg;
    string ans;

    if (isdigit(**st)) {
      int repeat_num = parse_repeat_num_(st);
      
      if (**st == '[') {
        stack_.push_back('[');
        (*st)++;
        ans = decodeString_(st);
        
        if (**st == ']') {
          stack_.pop_back();
          (*st)++; // point to the next one after ']';
          ans = makeup_partial_string(repeat_num, ans); 
          return ans;
        }
        throw runtime_error("Error: handleDigit error");
      } else {
        throw runtime_error("Error: handleDigit error");
      } 
    }
    return "";
}

// BNF: 
// <decode_string> ::= <decode_string>{, <decode_string>}  //1
// decode_string ::= [<digits> ] "[" <decode_string> "]"  //2
//
// 这个解法的decodeString_承担着2个职能,从handleStartWithDigit的调用, 
// 需要将结果返回给handleStartWithDigit;通过stack_计数的,则由stack的状态控制(因为同一层的decode_string可能有多个并列的递归定义);
// 这导致返回的出口有好几处,结合不同状态,这让代码很不清晰;
//
string Solution::decodeString_(string::const_iterator *beg) {
  auto *st = beg;
  string ans = "";
  
  do {
    if (isdigit(**st)) {
      ans += handleStartWithDigit(st);

      if (**st == ']') {
        // 一个handleStartWithDigit结构解析出(已经处理了digit对应的], 下一个仍是]), 返回上一层;
        return ans;
      }
    } else if (isalpha(**st)) {
      string tmp;
      alpha_label: 
      while (isalpha(**st)) {
        char c = **st;
        tmp += string(1, c);
        (*st)++;
      }
      ans += tmp;
      tmp = "";

      if (isdigit(**st)) {
        tmp = handleStartWithDigit(st);
        ans += tmp;
        tmp = "";
        goto alpha_label;
      }
      
      if (**st == ']') {
        // 字符结构的结束标志是], 返回调用decode_string_的上一层
        return ans;
      }
    }
  } while(stack_.size() != 0);

  return ans;
};

int main() {
    Solution solution;
    string s = "3[a]2[bc]";  
    string s1 = "3[a2[c]]"; 
    string s2 = "2[2[bc]]";
    solution.decodeString(s2);  // bcbcbcbc
    solution.decodeString(s1);  // accaccacc
    solution.decodeString(s);   // aaabcbc
    return 0;
}

解法2,3: 非递归实现

阅读了leetCode上的题解, 又写了以下2个非递归解法。入栈内容是数字和字符; 代码清晰了许多。 时间复杂度O(n)
空间复杂度最大也小于O(n);

解法2:

#include <iostream>
#include <stack>

using namespace std;

class Solution {
 public:
  string decodeString(const string &s) {
    int n = s.size();
    int num = 0;
    string current_str("");

    for (int i = 0; i < n; ++i) {
      char c = s[i];

      if (isdigit(c)) {
        num = num * 10 + (c - '0');
      } else if (c == '[') {
        num_stack_.push(num);
        num = 0;
        str_stack_.push(current_str);
        current_str = "";
      } else if (isalpha(c)) {
        current_str += string(1, c);
      } else if (c == ']') {
        string str_top = str_stack_.top();
        int num_top = num_stack_.top();
        str_stack_.pop();
        num_stack_.pop();

        string tmp = current_str;
        while (--num_top) {
          current_str += tmp;
        }
        current_str = str_top + current_str;
      }
    }

    cout << "result: current_str=" << current_str << endl;
    return current_str;
  }

  private:
    stack<int> num_stack_;
    stack<string> str_stack_;
};

int main() {
    Solution solution;
    string s = "3[a]2[bc]";
    string s1 = "3[a2[c]]"; 
    string s2 = "2[2[bc]]";
    solution.decodeString(s2);
    solution.decodeString(s1);
    solution.decodeString(s);
    return 0;
}

解法3:

#include <iostream>
#include <stack>
#include <string>

using namespace std;

class Solution {
 public:
  string decodeString(const string &s) {
    auto st = s.cbegin(), ed = s.cend();
    
    int num = 0;
    string current_str("");
	// 使用iterator与否并不重要;
    for (; st != ed; ++st) {
      char c = *st;

      if (isdigit(c)) {
        num = parse_repeat_num(&st);
      } else if (c == '[') {
        num_stack_.push(num);
        num = 0;
        str_stack_.push(current_str);
        current_str = "";
      } else if (isalpha(c)) {
        current_str += string(1, c);
      } else if (c == ']') {

        string str_top = str_stack_.top();
        int num_top = num_stack_.top();
        str_stack_.pop();
        num_stack_.pop();

        string tmp = current_str;
        while (--num_top) {
          current_str += tmp;
        }
        current_str = str_top + current_str;
      }
    }
    cout << "result: current_str=" << current_str << endl;
    return current_str;
  }

  private:
    stack<int> num_stack_;
    stack<string> str_stack_;

    int parse_repeat_num(string::const_iterator *beg) {
      auto st = *beg;
      string res("");
      while (isdigit(*st)) {
        res += *st;
        st++;
      }
      *beg = st - 1;
      return stoi(res);
    }
};

int main() {
    Solution solution;
    string s = "3[a]2[bc]";
    string s1 = "3[a2[c]]"; 
    string s2 = "2[2[bc]]";
    solution.decodeString(s2);
    solution.decodeString(s1);
    solution.decodeString(s);
    return 0;
}

gdb 调试tip

// 需要用-g, 生成符号表; 否则gdb会报No symbol table is loaded, error;
// g++会自动调用ld,使用linker生成binary可执行文件, gcc不会; 会报出undefined reference error
g++ -g -std=c++11 -Wall 394.cpp -o main_394
gdb -q main_394

其他: s, n, p, file. etc.

结果

在这里插入图片描述

总结

重申一下总结要点:

  • 此题的递归解法很难搞定, 所以注意某些情况是递归较容易,某些情况例如涉及状态机的情形,递归并不一定容易写出。此题在写解法1时,画了好复杂的一张状态转换图,并恰好直觉的将处理数字开头和‘[’开头的部分区分为2部分, 又发现必须利用栈来控制同一层字符中混有多个数字的情况,较顺利的得到结果。但尝试合并handleStartWithDigit和decodeString_2者并不成功,递归十分难写。
  • 结合具体例子,搞清怎么设计栈内容,入栈时机和出栈时机,是较快较容易解出本题的关键;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值