題意:
給定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;
}