栈+括号配对 51Nod1478 括号序列的最长合法子段

传送门:点击打开链接

题意:给一字符串只有括号,问最长合法子串的长度是多少,并输出这样的长度的子串有多少个。

思路:啊。。我发现这种括号配对的题我总是处理不好。

一开始想到了一种比较脑残的方法,首先(代表+1,)代表-1,我们维护前缀和s,然后我们用单调栈来维护最左端的满足[x,i]所有前缀和都>=s。

然后,我们并不能一直扩展到最左边,我们还应该记录前缀和等于s且后一个字符是(的位置。因为我们对于一个),向左匹配最多也只能匹配到这个位置。

于是,就有了下面这段傻逼代码

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef long long LL;

const int MX = 1e6 + 5;
const int MID = MX;

char S[MX];
int pos[2 * MX];
int Stack[MX], z[MX], ssz;
int main() {
    //FIN;
    scanf("%s", S + 1);
    int n = strlen(S + 1), now = 0;
    memset(pos, -1, sizeof(pos));

    Stack[++ssz] = 0; z[ssz] = 0;
    int maxlen = 0, cnt = 1;
    for(int i = 1; i <= n; i++) {
        if(S[i] == '(') now++;
        else now--;

        int p = i;
        while(ssz && now <= Stack[ssz]) p = z[ssz--];
        Stack[++ssz] = now; z[ssz] = p;
        if(S[i] == ')' && pos[now + MID] != -1) {
            int len = i - max(pos[now + MID], p);
            if(len > maxlen) {
                maxlen = len; cnt = 1;
            } else if(len == maxlen) cnt++;
        } else if(S[i] == '(' && pos[now + MID - 1] == -1) {
            pos[now + MID - 1] = i - 1;
        }
    }

    printf("%d %d\n", maxlen, cnt);
    return 0;
}



然后看了一下排行榜里别人的做法,实在是太简单易懂了~

我们来使用栈来匹配括号。如果为(,就压入栈。如果为),就取出栈顶,此时,从当前的位置,到栈顶表示的(的位置,这整个子串,一定是合法的。

然后如果在(的前面也有一段合法括号,那么两段就能合在一起,形成更长的。

所以代码非常简单,一个普通的栈,再加一个dp数组记录一下就ok了

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef long long LL;

const int MX = 1e6 + 5;

char S[MX];
int Stack[MX], dp[MX], ssz;

int main() {
    //FIN;
    scanf("%s", S + 1);
    int n = strlen(S + 1);
    int maxlen = 0, cnt = 1;
    for(int i = 1; i <= n; i++) {
        if(S[i] == '(') Stack[++ssz] = i;
        else if(ssz) {
            int t = Stack[ssz--];
            dp[i] = dp[t - 1] + i - t + 1;
            if(dp[i] > maxlen) maxlen = dp[i], cnt = 1;
            else if(dp[i] == maxlen) cnt++;
        }
    }
    printf("%d %d\n", maxlen, cnt);
    return 0;
}
啊,,下次再不会做括号匹配的题就药丸了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值