codeforces 1015F Bracket Substring

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎添加友链。 https://blog.csdn.net/zzk_233/article/details/83625965

题目大意:给出一个序列s,求出长度为2*n的序列中包含s的常规括号序列。常规括号序列就是指左右括号顺序和数量相等。

如果没有左右括号的限制,可以直接参考bzoj1009GT考试我的更新思路:https://blog.csdn.net/zzk_233/article/details/82852615

设dp[i][j]代表当前长度的串比配了j长度的方案数,更新的时候就是下一个字符为 "(" 或者 ")" ,匹配到的长度的值加上当前的值。

但是有了限制之后,可以将dp加至三维,表示有多少个左括号多余。当匹配到最后长度的时候,方案数就是当前的dp值乘上剩余

数量的字符中有和左括号同样多的右括号的情况。

f的转移就是这位如果是左括号,当前可以从f[i-1][j-1]转移,右括号可以从f[i-1][j+1]转移。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define mode 1000000007
using namespace std;
typedef long long ll;
char ss[205];
int n,l,nxt[205];
ll dp[205][205][205],f[205][205]/*长度为i没有匹配的左括号数量为j的个数*/,qr[205][2]/*已经匹配i个字符,下一个是1/0的最长长度*/;
void get_nxt()
{
	int t1=0,t2=-1;
	nxt[0]=-1;
	while(t1<l)
	{
		if(t2==-1||ss[t1]==ss[t2])
		{
			t1++;t2++;nxt[t1]=t2;
		}else
		{
			t2=nxt[t2];
		}	
	}
}
int gett(int x,int y)
{
	while(x!=-1&&ss[x]!=y+'(')
	{
		x=nxt[x];
	}
	return x+1;
}  
int main()
{
	scanf("%d",&n);
	scanf("%s",ss);
	l=strlen(ss);
	f[0][0]=1;
	for(int i=1;i<=n*2;i++)
	{
		for(int j=0;j<=i;j++)
		{
			if(j)f[i][j]+=f[i-1][j-1]%mode;
			f[i][j]+=f[i-1][j+1]%mode;
			f[i][j]%=mode;
		}
	}
	get_nxt();
	for(int i=0;i<l;i++)
	{
		qr[i][1]=gett(i,1);
		qr[i][0]=gett(i,0);
	}
	dp[0][0][0]=1;
	ll ans=0;
	int x=(ss[l-1]=='('?1:-1);//左括号为1 
    for(int i=0;i<=2*n;i++)
    {
	    for(int j=0;j<=i;j++)
	    {
	    	for(int k=0;k<=l;k++)
	        {	           
	            if(k==l&&2*n-i>=0)//当就剩一位的时候,前面多的左括号和后面的匹配,但是不能再往下更新dp,因为这是最后一位了,后面更新就会出现错误,所以continue。 
	            {
	                ans+=dp[i][j][k]*f[2*n-i][j]%mode;
	                ans%=mode;
	                continue;
	            }
	            dp[i+1][j+1][qr[k][0]]+=dp[i][j][k];
	            dp[i+1][j+1][qr[k][0]]%=mode;
	            if(j)(dp[i+1][j-1][qr[k][1]]+=dp[i][j][k])%=mode;
	        }
 	    }      
    }
      
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zzk_233/article/details/83625965