Codefights ReverseParentheses递归?

时间:2018-03-12 01:28:58

标签: c++ recursion

我在介绍中遇到Codefights Arcade问题13的问题。以下是问题陈述和我的代码到目前为止。我对如何解决问题的想法的轮廓是递归地在嵌套序列中向下/向内工作,当我到达单个序列(没有嵌套序列)反转它时,删除括号并返回它。递归的基本情况是一个字符串,没有序列,不需要反转,因此将返回调用堆栈。我现在有两个问题肯定,其中一个是一个大问题。我有一个无限的递归错误(下面的错误2)。无限递归的原因是,在co(de(fight)s)的情况下,一旦使用较短的字符串(de(fight)s)添加对堆栈的调用,它将在递增时删除“co”,但之后我就陷入无限递归(de(fight)s)作为字符串。我正在寻找这个问题的一些帮助。我想通过递归解决它,但我愿意接受其他想法。总的来说,我发现我仍然对递归很感兴趣并且正在努力改进它。

问题陈述

你有一个字符串s,包括英文字母,标点符号,空格字符和括号。保证s中的括号形成常规括号序列。

您的任务是从最里面的对开始反转每对匹配括号中包含的字符串。结果字符串不应包含任何括号。

实施例

对于字符串s =“a(bc)de”,输出应为 reverseParentheses(s)=“acbde”。

输入/输出

[执行时间限制] 0.5秒(cpp)

[输入]字符串s

由英文字母,标点符号,空格字符和括号组成的字符串。保证括号形成常规括号序列。

约束: 5≤s.length≤55。

以下是一些涉及专业的测试用例:

  1. a(bc)de - > acbde
  2. a(bcdefghijkl(mno)p)q - > apmnolkjihgfedcbq
  3. co(de(fight)s) - > cosfighted
  4. abc(cba)ab(bac)c - > abcabcabcabc
  5. [输出]字符串

    我的代码

    #include "stdafx.h"
    #include <string>
    #include <algorithm>
    
    using namespace std;
    
    bool hasSequence(string s) {
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == '(') {
                return true;
            }
        }
        return false;
    }
    
    bool hasSubSequence(string s) {
        int countOfLeftParen = 0;
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == '(') {
                countOfLeftParen++;
            }
        }
        if (countOfLeftParen > 1) {
            return true;
        }
        else {
            return false;
        }
    }
    
    string removeParentheses(string s) {
        s.erase(std::remove(s.begin(), s.end(), '('), s.end());
        s.erase(std::remove(s.begin(), s.end(), ')'), s.end());
        return s;
    }
    
    string reverseSequence(string s) {
        string toReverse = "";
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == '(') {
                while (s[i] != ')') {
                    toReverse += s[i];
                    i++;
                }
            }
        }
    
        //BUG1: bug here - this will reverse it, but it's not correct
        std::reverse(toReverse.begin(), toReverse.end());
        return toReverse;
    }
    
    
    std::string reverseParentheses(std::string s) {
        const char leftParen = '(';
        const char rightParen = ')';
        int leftParenCount = 0;
        int rightParenCount = 0;
        string needsReveresed;
    
        if (!(hasSequence(s))) {
            return s;
        }
    
        if (!(hasSubSequence(s))) {
            reverseSequence(s);
            return removeParentheses(s);
        }
    
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == leftParen) {
                leftParenCount++;
                while (rightParenCount < leftParenCount) {
                    needsReveresed += s[i];
                    i++;
                    if (s[i] == rightParen) {
                        needsReveresed += s[i];
                        rightParenCount++;
                        i++;
                    }
                    else {
                        if (s[i] == leftParen) {
                            needsReveresed += s[i];
                            leftParenCount++;
                            i++;
                        }
                    }
                }
                //BUG2: infinite recursion bug here. The string I pass down the recursion stack doesn't change
                s = reverseParentheses(needsReveresed);
            }
        }
        return s;
    }
    
    
    int main()
    {
        string input = "co(de(fight)s)";
        string result = reverseParentheses(input);
        return 0;
    }
    

3 个答案:

答案 0 :(得分:1)

好的,你问过除了递归之外还有其他方法可以解决这个问题。以下是使用std::stack<std::string>

的(建议)解决方案
#include <stack>
#include <string>
#include <algorithm>

int main()
{
    std::string s = "a(bcdefghijkl(mno)p)q";
    std::stack<std::string> stringStack;
    stringStack.push({});  // make sure we have one item in the stack.

所以基本上,我们从一个项目的堆栈开始,空字符串。然后我们浏览每个角色:

    for (size_t i = 0; i < s.length(); ++i)
    {
        if (s[i] == '(')
            stringStack.push({});

检查左括号的策略是创建一个新的子字符串。通过将全新的空字符串推入堆栈来完成子字符串的创建。

如果当前字符不是左括号,我们检查它是否是右括号。这是一些神奇的地方。

        else if (s[i] == ')')
        {
            // reverse the string at current stack top
            std::string topString = stringStack.top();
            std::reverse(topString.begin(), topString.end());

            // remove this string from the stack
            stringStack.pop();

            // append the string onto the current top of stack
            // or if stack is empty, make the reversed string the
            // top of stack.
            if (stringStack.empty())
                stringStack.push(topString);
            else
                stringStack.top() += topString;
        }

那我们在这做什么?我们检测到右括号表示“子串结束”。因此,我们反转当前位于堆栈顶部的字符序列,将反向字符串保存为临时字符串,然后弹出这些字符的堆栈。之后,我们将这些反转字符附加到堆栈的新顶部。

由于堆栈的顶部现在包含来自先前字符的构建字符串,因此这是我们的“字符串构建器”。堆栈顶部将始终包含处理结束时的最终字符串。

如果它既不是左括号也不是右括号,我们只是简单地连接到当前堆栈顶部的输入字符:

        else
            stringStack.top() += s[i];
    }
}

就是这样。没有删除括号,不检查我们是否已经在子序列中等等。

这是live example

请注意,除了您显示的测试用例之外,我没有尝试使用其他输入。如果存在未覆盖的边缘情况,则应能够轻松纠正上述代码以覆盖边缘情况。

答案 1 :(得分:1)

string reverseParentheses(string s) {
    int open = 0;
    int startInd = 0;

    for (int i = 0; i < s.length(); i ++){
        if (s[i] == '('){
            if (open == 0){
                startInd = i+1;
            }
            open++;
        }
        if (open == 1 && s[i] == ')'){
            string start = s.substr(0, startInd-1);
            string parens = s.substr(startInd, i - startInd);
            string revParens;
            for (int k = parens.length(); k >= 0 ; k--){
                if (parens[k] == '('){
                    revParens+=')';
                }
                else if (parens[k] == ')'){
                    revParens+='(';
                }else{
                revParens+=parens[k];
                }
            }
            string end = s.substr(i+1, s.length());
            return reverseParentheses(start + revParens + end);
        }
        if (s[i] == ')'){
            open--;
        }
    }
    return s;

}

答案 2 :(得分:0)

这是我使用递归和迭代器的方法。我必须先睡在上面。

void reverseStartingAt(std::string& working, std::string::iterator& itParenStart)
{
    auto itParenEnd = std::next(itParenStart);
    while (itParenEnd != working.end())
    {
        if (*itParenEnd == ')')
        {
            std::reverse(itParenStart, itParenEnd);                         // reverses ["(CBA)") ==> "ABC()"
            working.erase(std::prev(itParenEnd), std::next(itParenEnd));    // erases "ABC"["()x") ==> "ABCx"
            itParenStart = itParenEnd;                                      // Advance the start iterator to where the closing parenthesis was
            std::advance(itParenStart, -2);                                 // ... and subtract 2 to compensate for the removed "()"
            return;
        }
        else if (*itParenEnd == '(')
        {
            reverseStartingAt(working, itParenEnd);
        }
        itParenEnd++;
    }
}


std::string reverseInParentheses(std::string inputString)
{
    std::string working(inputString);
    for (auto itParenStart = working.begin(); itParenStart != working.end(); ++itParenStart)
    {
        if (*itParenStart == '(')
            reverseStartingAt(working, itParenStart);
    }

    return working;
}