【问题标题】:Minimum add to make parentheses string consisting of '{', '}', '[', ']', '(', ')' valid最小添加以使由 '{'、'}'、'['、']'、'('、')' 组成的括号字符串有效
【发布时间】:2021-01-20 21:48:34
【问题描述】:

这个问题是对熟悉的堆栈问题 (https://leetcode.com/problems/minimum-add-to-make-parentheses-valid/) 的补充,我们必须返回最小加法数才能使括号字符串有效。但是这个问题只包含'('和')'。如果我们将该问题扩展到其他类型的括号,例如“[”、“]”、“{”、“}”,将会发生什么。我刚刚在朋友之间的讨论中遇到了这个问题,需要有关如何处理的帮助。

例如:[[[{{}]]){)}) -> [[[{{}}]]] (){()}() 在这种情况下,答案是添加 5 个以使其有效。

我想不出合适的方法。我考虑的两种方法是:

  1. 与普通问题类似,我们在浏览字符串时将打开类型 '('、'{'、'[' 推送到堆栈,如果我们发现关闭类型 ')'、'}'、']'我们检查堆栈的顶部,如果它们相互补充,我们弹出并继续,否则我们增加计数器并继续而不弹出。遍历字符串后,我们将答案输出为计数器和堆栈大小的总和。在这种方法中,上面的示例将不起作用,因为额外的“{”会破坏该方法。

  2. 另一种方法与上述类似,即。我们推入括号的开始类型,如果我们找到一个结束类型并且如果堆栈的顶部补充它,我们弹出并继续字符串,否则我们将弹出直到我们得到一个匹配的字符串并且每次弹出我们增加计数器。遍历字符串后,总值是计数器和堆栈大小的总和。但这不适用于像 {{{{]}}}} 这样字符 ']' 会弹出所有内容并增加答案的情况。

我也在考虑将这些结合起来,更像是一个动态编程,我们将尽可能地查看最高值或查看直到我们在堆栈中找到匹配项或堆栈是否为空。但我不确定这两个是否是唯一需要考虑的情况。

【问题讨论】:

  • 我认为您的动态编程思想是正确的方法。我的建议是您为每种打开类型维护一个计数器,以跟踪当前堆栈中的数量。这样,当你找到一个关闭类型时,你就会知道堆栈中是否有匹配的。如果没有匹配,那么唯一的选择是增加添加的数量,并继续而不弹出。
  • 这是一个好主意,但是对于找到匹配的情况,我们必须将其弹出或在此处添加一个新字符,然后找出添加最少的字符?在那种情况下,我想它会变成 O(n^2) 。我会为此编写一个代码,然后我会尝试使用一些测试用例来破解它。我对这种方法唯一怀疑的部分是证明它总是有效的。
  • 是的,如果有匹配,代码需要尝试两个选项:要么弹出它,要么添加一个新字符。时间复杂度将取决于需要做出多少这样的决定。保留每种类型的计数器可以减少决策数量。

标签: string algorithm data-structures stack dynamic-programming


【解决方案1】:

只是贪婪地思考,对于每个结束标记都必须有一个开始标记,如果不是这样,那么我们必须添加一个开始括号。

所以我们可以通过遍历字符串并保持左括号的数量来找到最小的加法。因此,当我们遇到左括号时,我们会增加我们的计数变量,当我们遇到右括号时,如果我们有一些正数,我们会减少我们的计数变量,否则如果计数为零,这意味着我们必须在这里添加一个左括号。下面是这种贪婪方法的代码。时间复杂度 O(n) 和空间复杂度 O(1)。

int minAddToMakeValid(string s) {
    int cnt1  = 0 , cnt2 = 0, cnt3 = 0,  ans = 0;
    for(char ch : s){
        if(ch == '(')cnt1++;
        else if(ch == ')'){
            if(cnt1==0)ans++;
            else cnt1--;
        }
        if(ch == '{')cnt2++;
        else if(ch == '}'){
            if(cnt2==0)ans++;
            else cnt2--;
        }
        if(ch == '[')cnt3++;
        else if(ch == ']'){
            if(cnt3==0)ans++;
            else cnt3--;
        }
    }
    return ans + cnt1 + cnt2 + cnt3;
}

【讨论】:

  • 我们需要照顾{[吗?
  • @ShridharRKulkarni 很简单,您可以稍微扩展上述逻辑,而不是单个变量,您可以使用 3 个单独的变量。我确实更新了我的代码,你可以看看它。
  • 此解决方案不考虑定位:( [ { [ { ) } ] } ] 给出 0,但您至少需要 2 个括号来验证它
【解决方案2】:

说明

我们将逐个字符地处理我们输入的字符串,并更新目前遇到的括号的某些信息。对于每种括号类型,创建一个堆栈来保存未补偿的左括号的位置。基本上,它表示需要多少当前类型的右括号才能使字符串在检查时有效。

对于输入的每个括号,执行以下操作之一:

  1. 如果括号是开头的括号(任何类型),只需将其位置添加到相应的堆栈中。
  2. 否则,它是一个右括号。如果堆栈中没有左括号,只需增加结果总和 - 不平衡的右括号可以立即得到补偿。
  3. 最后,它是一个右括号,当前类型的堆栈中有左括号。因此,将位于同一类型的最后一个左括号和当前括号之间的其他类型的所有不平衡括号的数量相加!不要忘记从堆栈中删除匹配的元素。

最后,将每个堆栈的剩余大小添加到结果总和中,因为每种类型的左括号可能仍然不平衡。

代码

我在 C++ 中创建了一个简单的解决方案,但如果需要,它可以轻松转换为任何其他语言:

#include <iostream>
#include <stack>
#include <unordered_map>

bool isOpeningBracket(char bracket) {
    return bracket == '(' || bracket == '[' || bracket == '{';
}

int main() {
    std::string line;
    std::cin >> line;
    std::unordered_map<char, char> closingToOpeningBracket = {
            {')', '('},
            {']', '['},
            {'}', '{'}
    };

    std::unordered_map<char, std::unique_ptr<std::stack<uint64_t>>> bracketsMap;
    bracketsMap['{'] = std::make_unique<std::stack<uint64_t>>();
    bracketsMap['['] = std::make_unique<std::stack<uint64_t>>();
    bracketsMap['('] = std::make_unique<std::stack<uint64_t>>();
    uint64_t addOperations = 0;

    for(auto i = 0; i < line.size(); i++) {
        auto bracket = line[i];
        bool isOpening = isOpeningBracket(bracket);

        auto key = bracket;
        if (!isOpening) {
            key = closingToOpeningBracket[bracket];
        }
        auto &bracketStack = bracketsMap[key];
        if (isOpening) {
            bracketStack->push(i);
        } else if (!bracketStack->empty()) {
            auto openingBracketPosition = bracketStack->top();
            bracketStack->pop();

            for (auto & [key, value] : bracketsMap) {
                while (!value->empty() && value->top() > openingBracketPosition) {
                    addOperations++;
                    value->pop();
                }
            }
        } else {
            addOperations++;
        }
    }

    for (auto & [key, value] : bracketsMap) {
        addOperations += value->size();
    }

    std::cout << addOperations << "\n";

    return 0;
}

时间和空间复杂度

这个解的时间和空间复杂度是O(n)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 2015-05-17
    • 2017-07-27
    • 2013-04-21
    • 2019-07-13
    • 1970-01-01
    • 2013-10-08
    相关资源
    最近更新 更多