51Nod - 1478 单调栈 + 二分

题意:

找出一串括号序列中的最长的合法子串的长度以及数目。

思路:

如果对于括号序列,'('表示+1,')'表示-1,一串合法的括号序列要保证这样的前缀和序列首尾都是0,且序列中间的前缀和值不能小于0。对于这道题来说,找到每个前缀和sum[p]之后与之相邻最远的且相同的前缀和的位置q,那么[p+1,q]这一段就是以这个p+1开头的最长的合法括号序列。这里可以对于每个位置p利用单调栈求出第一个比当前位置的数小的位置,在这之间的相同前缀和肯定不会比sum[p]小,一开始要将所有相同的sum[i]的位置保存在vector里,然后对于单调栈求出的范围r[i]之内进行二分,找到最远的合法位置,并且更新最大值和数目即可。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
using namespace std;
const int MAXN = 1111111;
const int mid = 1000000;

char str[MAXN];
int sum[MAXN], cnt[MAXN], r[MAXN];
vector <int> pos[MAXN * 2];

int main() {
	scanf("%s", str + 1);
	int n = strlen(str + 1);
	sum[0] = 0;
	for (int i = 1; i <= n; i++) {
		sum[i] = sum[i - 1] + (str[i] == '(' ? 1 : -1);
		pos[mid + sum[i]].push_back(i);
	}
	stack <int> sta;
	int maxlen = 0;
	for (int i = n; i >= 0; i--) {
		while (!sta.empty() && sum[sta.top()] >= sum[i]) sta.pop();
		if (sta.empty()) r[i] = n + 1;
		else r[i] = sta.top();
		sta.push(i);
	}
	for (int i = 0; i <= n; i++) {
		int tmp = mid + sum[i];
		int p = lower_bound(pos[tmp].begin(), pos[tmp].end(), r[i]) - pos[tmp].begin();
		if (p > 0) {
			maxlen = max(maxlen, pos[tmp][p - 1] - i);
			cnt[pos[tmp][p - 1] - i]++;
		}
	}
	if (maxlen == 0) puts("0 1");
	else printf("%d %d\n", maxlen, cnt[maxlen]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值