【问题标题】:remove extra parenthesis去掉多余的括号
【发布时间】:2012-10-23 15:47:51
【问题描述】:

问题:

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

    ((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.

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

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) 空间内做到这一点吗?如果不是,最好的方法是什么?

【问题讨论】:

  • 当你说“额外的括号”时,你只是指有的地方 (( 还是你的意思是数学不需要它们的地方?例如 (a+b)+c 也产生+b+c 因为加法是可交换的。如果不是,只需将“(((”)“((”)替换为“(”,以及“)))”和“))”替换为“)”。跨度>
  • 我认为我给出的第二个示例输入消除了您的疑问。我不确定“只需将“(((”“((”)与“(”和“)))”和“))”替换为“)””的字符串即可
  • @FrankThomas 首先,这些例子表明数学没有考虑在内,只有双括号被替换。其次,这将失败。想想它会对((a + b) + (c + d))做些什么
  • @jasper,我相信它会返回 (a+b) + (c+d)。好点,这些也可以删除。
  • @FrankThomas 你提出了一个有趣的问题。对于浮点类型(可能也适用于有符号整数类型,但在任​​何常见架构上都不是这种情况),加法 not 关联,(a + b) + ca + b + c 相同,但在a + (b + c) 的情况下,您需要括号。或者他的问题可能没有明确说明:在 Windows 机器上,或者在大多数 Unices 上,在最后一种情况下,您不需要 int 的括号,但在某些奇异的机器上,您可能需要。

标签: c++ string map expression parentheses


【解决方案1】:

构建表达式树,然后重新生成具有最小值的表达式 括号。原文中没有的括号 生成是不必要的。

一个简单、几乎正确的解决方案是为 每个运营商。然后,您需要在任何时候直接使用括号 在您正在处理的节点下是一个具有较低 优先于节点;例如,如果您在 * (乘法)节点,两个子节点之一是+ (加法)节点。加上一些处理左右绑定的逻辑:如果 你在+ 节点,右手节点也是+ 节点,你 需要括号。

这只是部分正确,因为在 不能真正映射到优先语法的 C++:一些 我想到了类型转换构造或三元运算符。在 然而,至少在三元运算符的情况下,特殊处理 没那么难。

编辑:

关于你的大 O 问题:这显然不是 O(1) 空间,因为您需要在内存中构造整个表达式。一世 不要认为内存的 O(1) 是可能的,因为潜在地,你可以 无限嵌套,分不清括号是不是 是否有必要,直到一个不确定的时间之后。我其实没有 分析了它,但我认为它的时间是 O(n)。上界 节点数为n(字符串的长度),需要访问 每个节点只有一次。

【讨论】:

  • +1:从提交的代码来看,OP可能不熟悉运算符优先级解析,所以这可能是他/她的头,但它仍然是正确的答案。我不会担心 C++ 转换构造等; OPs 问题似乎只处理 id 和二元操作表达式(id 本身是一个一元表达式,但我不确定他们是否理解这一点)。另外,我看不到一元减号,我希望这不是暗示的。
  • @WhozCraig 我怀疑你对 OP 可能知道的内容是正确的。但这看起来很像家庭作业,目标可能是他实现一个简单的解析器来解决它。如果他不知道运算符优先级解析是一种技术或术语,他可能知道(或者他应该,如果他一直在学习这门课程)至少对优先级有一个直观的想法,并且知道如何编写一个简单的解析器来接受它考虑到。
  • @WhozCraig 和 FWIW,我从来没有提到运算符优先级解析;我只是说“构建一个表达式树”(这可以通过递归下降很容易地完成)。 pokey909 没有讲解析,但是他的伪代码是真正的优先解析。我真的很喜欢(即使它不能正确处理 ?: 运算符之类的情况)。
  • 我不知道你将如何减少你的表达式树没有优先决策。我当然需要一个。在这一点上,我认为你们俩都是对的。当我发表我最初的评论时,pokeys 的答案还没有出现,但现在我看到了,我同意你的看法。
  • @WhozCraig C++ 没有优先语法,并且有一些奇怪的角落(可能与这里无关)无法在优先语法中表达。 C++ 中没有优先级(但使用优先级表示它可能会有所帮助,只要不忘记奇怪的角)。
【解决方案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) > 优先级(peek(Q)),则推送(S, "Pop(S) y Pop(S)")
    • 如果优先级(y)
  6. S 之上的最终结果

所有都应该是 O(n)。

【讨论】:

    【解决方案3】:

    我想我会采取这种方式。这是我突然想到的问题的解决方案。请注意,这是伪代码,不能直接运行。

    (实际上,它更像是 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) 中构建输出字符串)

    【讨论】:

      猜你喜欢
      • 2020-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多