删除额外的括号

时间:2012-11-02 23:36:49

标签: c++ string map expression parentheses

问题:

从字符串中删除多余的括号 例如

    ((a+b))*c       => (a+b)*c  
    (a+b)+c         => (a+b)+c  
    ((a+b)/(c+d))   => ((a+b)/(c+d))   
    (a+(((b-c)))*d) => (a+(b-c)*d)  and so on.

我提出了以下解决方案:
方法:我扫描字符串并记住(使用地图)左括号的索引以及它是否是额外的(默认情况下是额外的)。如果我找到一个右括号,我从地图中检查相应的左括号,如果它是额外的,则删除两者。

void removeExtraParentheses(string& S){
  map<int, bool> pmap;
  for(int i = 0; i < S.size(); i++){
    map<int, bool>::iterator it;
    if(S.at(i) == '('){
        pmap[i] = true;
    }
    else if(S.at(i) == ')'){
        it = pmap.end();
        it--;
        if(!(*it).second){
            pmap.erase(it);
        }
        else{
            S.erase(S.begin() + i);
            S.erase(S.begin() + (*it).first);
            pmap.erase(it);
            i = i - 2;
        }
    }
    else{
        if(!pmap.empty()){
            it = pmap.end();
            it--;
            (*it).second= false;
        }
    }
  }
}  

时间复杂度:O(n2)
空间:O(n)
我对我的解决方案不太满意,因为我正在使用额外的存储并在二次时间内完成它。

我们可以在O(n)时间和O(1)空间中这样做吗?如果不是最好的人能做什么?

3 个答案:

答案 0 :(得分:2)

构建表达式树,然后用最小值重新生成表达式 括弧。原文中的任何括号都不在 生成是不必要的。

一个简单,几乎正确的解决方案是分配优先权 每个运营商。然后,您可以在任何时候直接使用括号 在您正在处理的节点下是一个较低的运算符 优先级高于节点的优先级;例如,如果你在* (乘法)节点,并且两个子节点之一具有+ (附加)节点。加上一些逻辑来处理左或右绑定:如果 您位于+节点,右侧节点也是+节点,您 需要括号。

这只是部分正确,因为有一些结构 C ++实际上不能映射到优先语法:其中一些 我想到了类型铸造结构,或三元运算符。在 但是,至少在三元运算符的情况下,特殊处理 并不难。

编辑:

关于你的大问题:这显然不是O(1)in space,因为你需要在内存中构造整个表达式。一世 不要认为O(1)的记忆是可能的,因为可能,你可以 有无限的嵌套,你不能告诉括号是否 必要与否,直到不确定的时间之后。我实际上并不是这样 分析它,但我认为它是O(n)及时。上限就是 节点数是n(字符串的长度),您需要访问 每个节点只有一次。

答案 1 :(得分:2)

或多或少在网上找到......

给定输入:((A + B)* C) 预期产出:(A + B)* C

假设:

  • Peek(队列)只是告诉队列前端的元素而不删除它。
  • precedence()是一个查找运算符优先级表的函数

下面的伪代码:

  1. 将中缀表达式转换为RPN(例如Shunting-yard algo O(n))

    AB+C*

  2. 仅在队列Q

    中插入运算符

    (前)+ -------- *(后方)

  3. 解析后缀表达式
  4. 如果是操作数,请按“S”堆叠
  5. 如果是运营商
    • Y =删除(Q)
    • 如果优先级(y)&gt;优先(peek(Q)),然后推(S,“Pop(S)y Pop(S)”)
    • 如果优先级(y)&lt;优先(peek(Q)),然后推(S,“(Pop(S)y Pop(S))”)
  6. 最终结果位于S
  7. 之上

    所有应该是O(n)。

答案 2 :(得分:0)

我以为我会刺伤这个。这是我想到的问题的解决方案。请注意,这是伪代码,并不意味着直接运行。

(实际上,它更像是C ++ - ish,但是自从我上次编写实际的C ++以来已经有一段时间了,当我打算通过一种算法时,我并不想努力使一切正确。)

queue<tuple<int, bool> > q = new queue<tuple<int, bool> >();

for (int i = 0; i < str.length; i++)
{
    char a = str.charAt(i);

    if (a == '(')
    {
        q.push(new tuple<int, bool>(i, false));
    }
    else if (a == ')')
    {
        if (q.top().bool)
        {
            // remove top.int and i from string
        }

        q.pop();
        q.top().bool = true;
    }
    else
    {
        q.top().bool = false;
    }
}

它在O(n)中完成工作并使用O(n)空间(实际上,使用的空间量实际上是基于字符串中最深的嵌套级别,但保证低于n

(请注意,// remove top.int and i from string实际上无法完成O(1)。但是,如果您获得了一点创意,则可以在O(1)中执行类似操作。 例如,您实际上可以为输出构建一个字符列表并存储迭代器而不是int,然后您可以删除O(1)中的两个字符。最后,您可以通过迭代O(n)中的列表来构建最终字符串。另一种解决方案是实际处理DummyOrCharacters的数组(或向量),它们是虚拟的,不包含任何内容或包含字符。再一次,你可以在O(1)中用假人替换一个角色。再次,您将遍历结构并在O(n)

中构建输出字符串