2301_80359693 2025-06-30 17:57 采纳率: 0%
浏览 16

求解答一个关于括号匹配的算法问题

题目

MT2114 - 括号家族

小码哥在研究只有左括号和右括号组成的序列。给出一个括号序列,求出最长合法子串和它的数量(合法的定义:这个序列中左右括号匹配)。

例如:(()不合法,)()(也不合法,但()()(())合法。

格式

输入格式:

输入一行只有左括号和右括号组成的序列(只包含小括号)。

输出格式:

输出两个数字:最长子字符串的长度,以及此类子字符串的数量,如果没有这样的子字符串,则输出0 1

样例 1

输入:

()()))()()(()

输出:

4 2
备注

其中:字符串长度小于等于$10^6$。

疑问:

我的代码只通过了三个(测试结果如下图),但是我自己找不出反例。。有没有人能帮忙看看。(最好能给出反例)

img

思路说明:

总体是用了线性DP
dp数组根据括号种类有两种含义:

对于左括号'(':dp[i]表示该左括号前面邻接的已成功匹配的长度

对于右括号')':dp[i]表示如下:

如果该右括号没有匹配成功,则dp[i]=-1;

否则:
设j为与该右括号匹配的左括号索引,则i+1-j为这对括号的长度,
则dp[i]=i+1-j+dp[j]表示这对括号的长度加上前面邻接的已成功匹配的长度。应该就是以该右括号结尾的成功匹配的最长子串长度。

#include<bits/stdc++.h>
using namespace std;
int main(){
    string s;
    cin>>s;
    long long n=s.size();
    stack<long long> stk;
    vector<long long> dp(n,-1);
    long long max=0,cnt=1;
    long long len=0;//len用来统计邻接下一个左括号的成功匹配的长度
    for(long long i=0;i<n;i++){
        if(s[i]=='('){
            if(i>0&&s[i-1]=='('){
                len=0;//如果前面也是左括号则该左括号前面邻接的成功匹配长度为0
            }
            stk.push(i);//将左括号索引入栈
            dp[i]=len;
        }
        else{
            if(stk.empty()) {//栈空时遇到右括号就跳过同时len=0
                len=0;
                continue;
            }
            else{//栈非空该右括号就被匹配到索引为stk.top()的左括号
                dp[i]=i-stk.top()+dp[stk.top()]+1;
                stk.pop();
                len+=2;
            }
            //下面是统计max和cnt
            if(dp[i]>max){
                max=dp[i];
                cnt=1;
            }
            else if(dp[i]==max) cnt++;
        }
    }
    cout<<max<<" "<<cnt<<endl;
    return 0;
}
  • 写回答

7条回答 默认 最新

  • 檀越@新空间 2025-06-30 17:58
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    你的问题是一个经典的括号匹配问题,目标是找出最长合法子串的长度以及该长度出现的次数。你提供的代码思路是基于**栈 + 动态规划(DP)**的方法,但存在一些逻辑错误,导致无法通过所有测试用例。


    ✅ 问题分析

    1. 错误原因

    • 你在处理右括号时,使用了一个 len 变量来统计邻接的已成功匹配长度,但这个变量的逻辑并不正确。
    • dp[i] 的定义不够清晰,且在处理右括号时没有完全利用 DP 数组的结构。
    • 在计算 cnt 时,只考虑了当前右括号结尾的情况,忽略了可能有多个不同位置的相同最大长度子串。

    ✅ 正确思路

    🔹 解题思路(推荐方法)

    我们可以采用以下步骤:

    1. 使用一个栈来记录左括号的位置。
    2. 使用一个数组 dp 来记录以每个位置结尾的最长合法子串长度。
    3. 遍历字符串,遇到左括号则入栈,并更新 dp[i] = 0(因为单独一个左括号不能构成合法子串)。
    4. 遇到右括号时:
      • 如果栈不为空,则弹出栈顶元素(即匹配的左括号索引),并计算当前匹配的长度为 i - top + 1 + dp[top]
      • 更新 dp[i] 的值。
    5. 同时维护一个全局变量 max_lencount,用于记录最长合法子串的长度和其出现次数。

    ✅ 修改后的代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        string s;
        cin >> s;
        long long n = s.size();
        stack<long long> stk;
        vector<long long> dp(n, 0); // dp[i] 表示以 i 结尾的最长合法子串长度
        long long max_len = 0;
        long long count = 0;
    
        for (long long i = 0; i < n; ++i) {
            if (s[i] == '(') {
                stk.push(i);
                dp[i] = 0; // 单独左括号不能构成合法子串
            } else {
                if (!stk.empty()) {
                    long long left_idx = stk.top();
                    stk.pop();
    
                    // 计算当前合法子串长度
                    dp[i] = i - left_idx + 1;
                    if (left_idx > 0) {
                        dp[i] += dp[left_idx - 1]; // 加上前面的合法子串长度
                    }
    
                    // 更新最大长度和计数
                    if (dp[i] > max_len) {
                        max_len = dp[i];
                        count = 1;
                    } else if (dp[i] == max_len) {
                        count++;
                    }
                } else {
                    dp[i] = 0; // 不合法,重置
                }
            }
        }
    
        if (max_len == 0) {
            cout << "0 1" << endl; // 没有合法子串
        } else {
            cout << max_len << " " << count << endl;
        }
    
        return 0;
    }
    

    ✅ 示例解析

    输入:

    ()()))()()(()

    输出:

    4 2

    解释:

    • 合法子串有:
      • "()()"(从第0位到第3位)
      • "()()"(从第4位到第7位)
    • 最长长度为 4,出现 2 次。

    ✅ 常见反例及说明

    反例 1:

    输入:)()())

    输出应为:4 1(合法子串是 "()()"

    反例 2:

    输入:((()))

    输出应为:6 1(整个字符串都是合法的)

    反例 3:

    输入:()(())

    输出应为:6 1(整个字符串合法)


    ✅ 总结

    | 问题点 | 修正方式 | |--------|----------| | len 变量逻辑错误 | 使用 dp[i] 来记录以当前位置结尾的合法子串长度 | | dp[i] 定义不清 | 明确定义为“以 i 结尾的最长合法子串长度” | | count 统计不全面 | 需要遍历所有 dp[i] 才能统计所有最大长度子串 |


    如果你还有其他疑问或想了解更优化的解法(如线性时间复杂度),欢迎继续提问!

    评论

报告相同问题?

问题事件

  • 创建了问题 6月30日