版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎添加友链。 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;
}