51nod 1478 括号序列的最长合法子段【思维+前缀和+优先队列】好题!

本文介绍了一种解决寻找字符串中最长合法括号序列及其数量的方法,通过预处理字符赋予数值,利用栈和优先队列维护合法子串,最终实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目来源:  CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 40  难度:4级算法题


这里有另一个关于处理合法的括号序列的问题。

如果插入“+”和“1”到一个括号序列,我们能得到一个正确的数学表达式,我们就认为这个括号序列是合法的。例如,序列"(())()", "()"和"(()(()))"是合法的,但是")(", "(()"和"(()))("是不合法的。

这里有一个只包含“(”和“)”的字符串,你需要去找到最长的合法括号子段,同时你要找到拥有最长长度的子段串的个数。


Input
第一行是一个只包含“(”和“)”的非空的字符串。它的长度不超过 1000000。
Output
输出合格的括号序列的最长子串的长度和最长子串的个数。如果没有这样的子串,只需要输出一行“0  1”。
Input示例
)((())))(()())
Output示例
6 2

思路:


1、对于括号匹配问题,我们可以用+1和-1来代替左括号以及右括号。

那么对于(我们赋值为1

那么对于)我们赋值为-1

辣么对于一组样例:

((())(()((

我萌可以处理出来一个sum【i】;表示赋值的前缀和:

1 2 3 2 1 2 3 2 3 4


2、接下来我们讨论一下什么是合法子串:

对于一个括号匹配合法子串【l,r】【这里l必须是左括号,r必须是右括号】,其左括号数==右括号数&&对于区间【l,i】(l<=i<=r)不存在左括号少于右括号的情况存在;

①对于第一个条件:其左括号数多余右括号数,映射到sum【i】上来反映就是说:sum【l】=sum【r】-1;

对于上述例子中,符合条件的有很多。比如:【2,5】:sum【2】=2,sum【5】=1;不难发现这个子串是一个合法子串。

再比如:【3,8】,sum【3】=3;sum【8】=2;但是发现这个子串并不是一个合法子串,因为在【3,5】的区间中,右括号的数目多余左括号的数目。

那么我们再探究第二个条件:

②对于第二个条件:对于区间【l,i】(l<=i<=r)不存在左括号少于有括号的情况存在;映射到sum【i】上来反映就是:sum【i】>=sum【l】-1;(l<=i<=r),必须每一个i都满足才行。


3、对于一个合法子串的判定我们已经探讨完毕,那么接下来我们肯定就是开始维护统计最长合法子串长度以及数目了。

那么我们接下来的任务就是维护这个过程了。

我们肯定一点,作为左括号,其只能作为一个合法子串的开端或者是中间部分,肯定不是结尾。反之,作为右括号,要么是中间部分,要么是结尾。

而且我们希望找出来的子串最长,那么我们维护一个数组pos【i】,表示pos【i】为空的时候sum【i】第一次出现的位子。对应出现一个左括号,我们就对其进行维护。

对应出现一个右括号,我们就找sum【i】+1的第一次出现的最左边的合法位子。

在过程中,我们为了保证:区间【l,i】(l<=i<=r)不存在左括号少于右括号的情况存在的条件。那么每次出现一个右括号,那么我们就应该对应将大于等于sum【i】+2部分的pos【i】重新置为空。

然而如果暴力置空的话肯定会TLE掉,那么我们再设定一个优先队列(最大值优先),将每次出现的左括号同时pos【sum【i】】为空的sum【i】丢进去。

那么在出现右括号的时候,我们将大于等于sum【i】+2的部分pop掉,同时将这部分都重新置为空。。


过程一直维护下去即可。


4、思路比较冗杂,更详细的部分参考代码。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<stack>
#include<queue>
using namespace std;
char a[1000060];
int sum[1000060];
int pos[3000060];
int main()
{
    while(~scanf("%s",a))
    {
        priority_queue<int >que;
        stack<int >s;
        int maxn=0;
        int ans=1;
        memset(pos,-1,sizeof(pos));
        int mid=1500030;
        int n=strlen(a);
        for(int i=0; i<n; i++)
        {
            if(a[i]=='(')
            {
                if(i==0)sum[i]=1;
                else sum[i]=sum[i-1]+1;
            }
            else
            {
                if(i==0)sum[i]=-1;
                else sum[i]=sum[i-1]-1;
            }
        }
        for(int i=0;i<n;i++)
        {
            if(a[i]=='(')
            {
                if(pos[sum[i]+mid]==-1)
                {
                   que.push(sum[i]);
                   pos[sum[i]+mid]=i;
                }
            }
            else
            {
                if(pos[sum[i]+1+mid]!=-1)
                {
                    if(i-pos[sum[i]+1+mid]+1>maxn)
                    {
                        maxn=i-pos[sum[i]+1+mid]+1;
                        ans=1;
                    }
                    else if(i-pos[sum[i]+1+mid]+1==maxn)
                    {
                        ans++;
                    }
                }
                while(!que.empty())
                {
                    if(que.top()>=sum[i]+2)
                    {
                        pos[que.top()+mid]=-1;
                        que.pop();
                    }
                    else break;
                }
            }
        }
        printf("%d %d\n",maxn,ans);
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值