题解 | 最长有效括号

题干分析:

题目要求我们针对一个只含有'('和')'字符的字符串中选取最长的可匹配的括号字串,返回子串的长度.

首先关于括号匹配,由于匹配的括号一定为一对,因此我们可以确定这个返回值是个偶数(虽然基本没啥用)

算法思路:

我们有至少两个思路.

方法一便是直接模拟,我们创建一个既能存储整数,又能存储字符的栈(可以使用结构体),我们扫描整个字符串,

遇到'('便压栈,

遇到')':

首先判断栈顶是否为'(',

如果是,则匹配成功,将栈顶元素弹出,创建一个整数2表示已匹配字符串长度,然后持续弹出栈顶为数字的元素并相加,直到栈顶再次为字符,保证整数合并;

如果栈顶不是'('但是是数字,我们弹出该数字暂存,然后判断下一个字符是否为'(',

如果是则匹配成功,暂存数字+2,之后持续合并数字后压栈,

如果不是,暂存数字压栈,字符')'也压栈;

同时其他情况(栈顶为')')也直接压栈.

最后持续弹栈记录栈中最大的整数即为可匹配的最长括号字串.

方法二则更加巧妙:

我们已知符合要求的字串一定是'('的数量=')'的数量,我们假设从左侧开始扫描字符串并持续分别记录两种字符串的数量,当我们一旦发现两种字符数量相等便记录一下最大值.但我们也得注意,从左侧开始扫描只有"()"是合法的匹配方式,换句话说")("是不视为匹配发生的,因此扫描过程中但凡发现')'的数量多于'('的数量,证明无法匹配,两者均归零,此时开始就是下一个能够匹配的字串长度了.

同时我们还得注意,此方法到此为止并不能判断'('的数量多于')'时的情况,我们将上述从左到右扫描的方式反过来,再次进行扫描记录便能解决这个问题,虽然这样做会做些许重复工作,但最终结果是正确的.

实现代码:

struct CI {
        int i; // 整数表示已匹配成功的括号组
        char c;
        bool isInt;

        explicit CI(const int _i) : i(_i), c(0), isInt(true) {
        }

        explicit CI(const char _c) : i(0), c(_c), isInt(false) {
        }
    };

int longestValidParentheses(string s) {
        stack<CI> st;
        for (auto c: s) {
            // '('直接压栈
            if (c == '(') st.emplace(c);
            else {
                // ')'且栈顶为'('
                if (!st.empty() && st.top().c == '(') {
                    st.pop();
                    CI tmp(2);
                    // 持续合并整数
                    while (!st.empty() && st.top().isInt) {
                        tmp.i += st.top().i;
                        st.pop();
                    }
                    // 压入整数
                    st.push(tmp);
                } else if (!st.empty() && st.top().isInt) {
                    // ')'且栈顶为整数
                    CI tmp = st.top();
                    st.pop();
                    // 栈顶元素为'('
                    if (!st.empty() && st.top().c == '(') {
                        st.pop();
                        tmp.i += 2;
                        // 持续合并整数
                        while (!st.empty() && st.top().isInt) {
                            tmp.i += st.top().i;
                            st.pop();
                        }
                        st.push(tmp);
                    } else {
                        st.push(tmp);
                        st.emplace(c);
                    }
                } else st.emplace(c);
            }
        }
        int _max = 0;
        while (!st.empty()) {
            if (st.top().isInt) _max = max(_max, st.top().i);
            st.pop();
        }
        return _max;
    }
int longestValidParentheses(string s) {
        int maxLen = 0, left = 0, right = 0;
        const int n = s.length();

        // 从左到右:统计左右括号数量
        for (int i = 0; i < n; ++i) {
            if (s[i] == '(') left++;
            else right++;

            if (left == right) maxLen = max(maxLen, 2 * left);
            else if (right > left) left = right = 0; // 无效,重置
        }

        // 从右到左:处理左括号多余情况
        left = right = 0;
        for (int i = n - 1; i >= 0; --i) {
            if (s[i] == '(') left++;
            else right++;

            if (left == right) maxLen = max(maxLen, 2 * left);
            else if (left > right) left = right = 0; // 无效,重置
        }

        return maxLen;
    }

复杂度分析:

针对方法一:

每个元素最多进出栈一次,辅助判断的整数消耗的也是常量级别的时间与空间,因此时间复杂度与空间复杂度均为O(n).

针对方法二:

需要对字符串进行两次扫描,对每个字符进行操作只需要常数的时间,因此时间复杂度为O(n).

全过程只使用到了常数的额外空间,因此空间复杂度为O(1).

全部评论

相关推荐

菜菜狗🐶:双非之光
找工作,你会甘心进小厂还...
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务