题目大意:
小Y 最近开始学习算法姿势,但是因为小R 非常BB,给了她很多B6 题,所以她觉得自己已经没有什么前途了。于是小R 给了她一些稍微简单的题,让她觉得已经没有什么好害怕的了,其中一道是这样的:
给定一个长度为n 只包含左括号和右括号的序列,现在小R 想要知道经过每一个位置的合法子串有多少个。
空串是一个合法的串,如果A 和B 都是合法的串,那么(A) 和AB 都是合法的串。
解题思路:
把左括号视为
1
1
1,右括号视为
−
1
-1
−1,处理处前缀和数组
w
w
w。子串
[
L
,
R
]
[L, R]
[L,R] 满足条件当且仅当
w
L
−
1
=
w
R
w_L−1 = w_R
wL−1=wR 且对所有的 L ≤ i ≤ R 都有
w
i
≥
w
R
w_i ≥ w_R
wi≥wR。
那么我们可以先把所有位置按照
w
w
w 权值从大到小排序(桶排),然后建一个
双向链表,开始所有节点都在链表中,从大到小(值相同先考虑位置较大的)考虑每一个点,
此时它的前驱一定是在它前面第一个权值小于等于它的点,这样就可以求出以每一个点为右端
点最短的满足条件的串是什么。得到了这个之后随便递推一下就可以得到
a
n
s
ans
ans 数组了。
时间复杂度
O
(
n
)
O(n)
O(n)
A c c e p t e d c o d e : Accepted\ code: Accepted code:
#include<stack>
#include<cstdio>
#include<cstring>
#define int long long
#define iint int
#define Int register int
#define Mem(x) memset(x, 0, sizeof(x))
using namespace std;
const int Mod = 1e9 + 7;
const int Maxn = 1e6 + 5;
iint T, N, len, p, q;
iint l[Maxn], r[Maxn], pre[Maxn], nxt[Maxn];
int ans[Maxn], sum;
char c[Maxn];
void write(int x) {
if (x > 9) write(x/10); putchar(x%10+48); return;
}
void writeln(int x) {
write(x); putchar(10);
}
signed main() {
stack <int> s;
scanf("%d", &T);
while(T--) {
while(s.size()) s.pop();
sum=0; Mem(l); Mem(r);
Mem(ans); Mem(pre); Mem(nxt);
scanf("%s", c + 1);
N = strlen(c + 1);
for (Int i = 1; i <= N; ++i) {
if (c[i] == '(') s.push(i); else
if(s.size()) nxt[s.top()] = i + 1, pre[i+1] = s.top(), s.pop();
}
for (Int i = N + 1; i > 0; --i) r[pre[i]] += ++r[i];
for (Int i = 1; i <= N; i++) l[nxt[i]] += --l[i];
for (Int i = 1; i <= N; i++) {
ans[i] = ans[~-i] + l[i] + r[i];
sum += (ans[i] * i) % Mod;
}
writeln(sum);
}
}