我在介绍中遇到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。
以下是一些涉及专业的测试用例:
[输出]字符串
我的代码
#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;
}
答案 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;
}