CF div2 #343 ----C. Famil Door and Brackets dp

别人口中的“水题” 虽然我觉得是有点难度,挺死脑细胞的。
好吧,不废话了,讲题意: 给出一个长度为m 的字符串s,(由左右括号组成),现要得到一个长度为n的字符串,所以要在s 的前面和后面分别加上字符串 p,q。 -> p+s+q !
有两个要求:
1. 左括号数目=右括号数目。
2.字符串任何一个前缀都必须满足左括号数目>= 右
一开始没懂2 是什么意思,所以真的很难想求解的方法。实际上 前缀的意思 就是这个字符串的前多少个! 在这个字符串的前任意个字符里面都需要满足 左>=右 ;

好了,现在可以思考一波了:
既然 是由 p + s+ q 组成, 所以p和 q 是随意选择左右括号组成的 只要形成的最后字符串满足1,2;
我们可以知道 只要确定p 那么q 就确定下来了。

对p 以左括号以及右括号组成的字符串,且需要满足2。p的长度可以是从0到n-m, 那么我们可以处理对于前缀为i(也就是前i个字符),他的前缀和(也就是左括号数目-右括号数目)从0到 i 的种数.
比如
长度 i,前缀和j对应的种数:
i=1 ,j=0的处理 dp[i][j]=0; j=1 dp[i][j]=1 (q=”(“); 但是取不到-1,因为任意前缀和>=0,所以dp>=0;
i=2;j=0, dp[i][j]=1; (p=”( )”); j=1 dp[i][j]=0 ;(没有这种p) ; j=2 dp[i][j]=1 (p=”( (“).

仔细想一下 ,其实不难得出:
对于 j=0;也就是左括号=右括号有:
dp[i][0] = dp[i-1][1]; 因为前i-1个字符 形成左括号-右括号=1,从而第i个 可以是)。
而对于j>0,即左括号- 右括号=j,有:
dp[i][j]=dp[i-1][j+1] + dp[i][j-1];
因为只有前i-1 个字符前缀和为 j+1或者j-1 的时候我们才可以使得前i字符串的前缀和为j,从而就可以求出种数!

预处理完 p和q 种数了,我们就可以进行处理了,我们还需要知道对于 字符串s 的最小前缀和,以及s的整个字符串的前缀和 flag。
因为我们要满足 对于i,dp[i][j] 有 j+最小前缀和>=0 并且种数 = dp[i][j] ( p的种数) * dp[n-m-i][flag+j](q的种数)

最后说一句,感觉是个铜牌题,Orz 原谅我这么弱TUT。
贴上代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#define mem(a) memset(a,0,sizeof(a))
#define pfn printf("\n")
#define sf scanf
#define pf printf
#define fr(i,n) for(int i=0;i<n;i++)
#define INF 0x7fffffff //INT_MAX
#define inf 0x3f3f3f3f //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
using namespace std;
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1

const int MOD=1000000007;
const int maxn=100050;
char s[maxn];
__int64 dp[2050][2060];
int main(){
    //freopen("1.txt","r",stdin);
    int n,m,i,j;
    while(scanf("%d%d",&n,&m)!=EOF){
        scanf("%s",s+1);
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(i=1;i<=n-m;i++){
            for(j=0;j<=i;j++){
                if(!j)
                    dp[i][j]=dp[i-1][1];
                else{
                    dp[i][j]=(dp[i-1][j-1]+dp[i-1][j+1]) %MOD;//讲道理这里应该%
                }
            }
        }
        int minx=inf;
        int flag=0;
        __int64 ans=0;
        for(i=1;i<=m;i++){
           if(s[i]=='(')
               flag++;
           else
               flag--;
           minx=min(minx,flag);
        }
        for(i=0;i<=n-m;i++){
            for(j=0;j<=i;j++){
                if(j+minx>=0 && i+m+j+flag<=n )
                    ans=(ans+dp[i][j]*dp[n-m-i][j+flag])%MOD;
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(CF div2 #343 ----C. Famil Door and Brackets dp)