【DP】【KMP】Bracket Substring CodeForces - 1015F

本文介绍了一种求解括号序列匹配方案数目的算法,通过动态规划预处理方案数,并利用KMP算法进行状态转移优化,最终计算出模1000000007下的匹配方案总数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

给出一个括号序列,求包含这个序列且长度为 2*n 的括号匹配的方案数,模1000000007。
1≤n≤100,1≤|s|≤200。

分析

预处理出 f [ i ] [ j ] : 长度为 i ,还没有匹配的左括号的个数为 j 的方案数。
f [ 0 ] [ 0 ] = 1 ; f [ i ] [ j ] = f [ i - 1 ] [ j - 1 ] (在i 位置放‘(’) + f [ i - 1 ] [ j + 1 ](在 i 位置放‘)’);
定义 dp [ i ] [ j ] [ k ] : 长度为 i ,还没有匹配的左括号的个数为 j,与题目给出的括号序列匹配了前 k 个字符的方案数 ,
考虑 dp [ i ] 对 dp [ i + 1 ] 的贡献,需要用 KMP 预处理出 qr [ k ] [ 0 ] 和 qr [ k ] [ 1 ] ;
qr [ k ][ 0 ] 表示已匹配 k 个字符,加上一个 ‘(’ 能匹配给出的括号序列的最长长度;
qr [ k ] [ 1 ]:加上‘)’;
状态转移:

dp[i+1][j+1][qr[k][0]]+=dp[i][j][k];
if(j) dp[i+1][j-1][qr[k][1]]+=dp[i][j][k];

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 210
#define MO 1000000007
#define LL long long
int n;
LL f[MAXN][MAXN],dp[MAXN][MAXN][MAXN];
void Pre()
{
    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];
            f[i][j]+=f[i-1][j+1];
            f[i][j]%=MO;
        }
}
char s[MAXN];
int fail[MAXN],a[MAXN],len,qr[MAXN][2];
void GetFail()
{
    len=strlen(s);
    int j=0,i=1;
    fail[0]=-1;
    while(i<len)
    {
        if(j==-1||s[i]==s[j])
            fail[++i]=++j;
        else j=fail[j];
    }
    for(int i=0;i<len;i++)
    {
        a[i+1]=(s[i]==')');
        for(int j=0;j<2;j++)
        {
            int x=i;
            while(x!=-1)
            {
                if(a[x+1]==j)
                {
                    qr[i][j]=x+1;
                    break;
                }
                x=fail[x];
            }
        }
    }
}
int main()
{
    scanf("%d",&n);
    Pre();
    scanf("%s",s);
    GetFail();
    LL ans=0;
    int x=(a[len]==0?1:-1);
    dp[0][0][0]=1;
    for(int i=0;i<=2*n;i++)
        for(int j=0;j<=i;j++)
            for(int k=0;k<len;k++)
            {
                if(j+x>=0&&k==len-1&&2*n-i>0)
                {
                    ans+=dp[i][j][k]*f[2*n-i-1][j+x];
                    ans%=MO;
                }
                dp[i+1][j+1][qr[k][0]]+=dp[i][j][k];
                dp[i+1][j+1][qr[k][0]]%=MO;
                if(j) (dp[i+1][j-1][qr[k][1]]+=dp[i][j][k])%=MO;
            }
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值