题意:
给定M≤N≤105,N−M≤2000,M为原始括号序列s的长度,现要求寻找p,q括号序列
要求|p+s+q|=N,且新序列左右括号数相等,并且任意前缀左括号数大于右括号数
求合法的(p,q)方法数
分析:
考虑dp,括号序列经典的表示方式就是平衡,f[i][j]:=长度i平衡为j的合法括号序列数
转移的时候首先要求括号序列合法(即任意前缀的平衡非负),其次直接根据平衡转移就可以了
预处理出f之后,来累加一下题目要求的(p,q)答案
对于p,要满足p+q括号序列合法(即任意前缀的平衡非负)
假设p的平衡为j,q的平衡为b,最小平衡为minB,显然要满足j+minB≥0
并且由于后面还有q,所以要留够长度,满足j+b≤min(i+m,n−i−m)
剩余的q的平衡应该是−(j+b),由于对称性,乘f[n−i−m][j+b]即可
时间复杂度为O((n−m)2)
代码:
//
// Created by TaoSama on 2016-02-22
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, m;
char s[N];
int f[2005][2005];
void add(int& x, int y) {
if((x += y) >= MOD) x -= MOD;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d%d%s", &n, &m, s + 1) == 3) {
f[0][0] = 1;
for(int i = 1; i <= n - m; ++i) {
for(int j = 0; j <= i; ++j) {
if(j - 1 >= 0) add(f[i][j], f[i - 1][j - 1]); //(
if(j + 1 <= i) add(f[i][j], f[i - 1][j + 1]); //(
}
}
int b = 0, minB = INF;
for(int i = 1; i <= m; ++i) {
if(s[i] == '(') ++b;
else --b;
minB = min(minB, b);
}
int ans = 0;
for(int i = 0; i <= n - m; ++i)
for(int j = 0; j <= i; ++j)
if(j + minB >= 0 && j + b <= min(i + m, n - m - i))
add(ans, 1LL * f[i][j] * f[n - m - i][j + b] % MOD);
printf("%d\n", ans);
}
return 0;
}