【补题日记】[2022牛客暑期多校2]K-Link with Bracket Sequence I

本文介绍了一种使用动态规划解决字符串LCS(最长公共子序列)问题的方法,特别针对包含括号的序列,通过状态转移方程详细解释了代码实现和状态理解。讨论了dp数组下标的选择策略以及状态更新的逻辑。

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

Pro

https://ac.nowcoder.com/acm/contest/33187/K

Sol

代码参考了std的写法,因为的确是不会写的dp,讲题也是一句话带过了

f i , j , k f_{i,j,k} fi,j,k表示序列b的前i位与序列a的LCS长度为k,且左括号减右括号个数为j的方案数。

状态转移方程可以在下面程序中很容易看出来,不过可能不太好理解

为什么还要有else,直接放到另一种括号里不可以吗?我们通过k的取值范围可以看出来,if判断是否为左右括号,其判断的整个str序列以及之后的空的部分,所以如果此处不为左(右)括号时,可能是已经将“a的序列是b的子序列”条件考虑完毕,即a序列遍历完毕,则之后不为左(右)括号。基于这种情况的状态转移方程,此时LCS的长度不再发生变化仍然为k,因此更新时第三维为k,又可以根据括号的左右可以得到第二维(左括号-右括号)的更新关系(加一或减一)。

除此之外的另一种情况,即a序列未被遍历完毕,此时考虑下一个字符为左括号或右括号,同理根据左右括号关系考虑加一减一,以及LCS长度的变化。

关于下标的问题:下面代码中很多地方采用了下标加1的方式,是因为循环是从0开始的,防止下标越界,而仔细来看可以发现:第一维都是由i更新i+1的值,这符合我们常规的dp;第二维有j更新j-1或者j更新j+1,这是因为左右括号数量之差与第一维的循环并不存在线性关系,因此只能根据此处为左括号还是右括号更新第二维;第三维有k更新k和k更新k+1两种情况,上文已说,两种情况分别对应a序列已经遍历完毕和a序列尚未遍历完毕两种情况。而至于下标加1,是因为简化了处理开头和避免了负数下标的情况。

Code

//By cls1277
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define Fo(i,a,b) for(LL i=(a); i<=(b); i++)
#define Ro(i,b,a) for(LL i=(b); i>=(a); i--)
#define Eo(i,x,_) for(LL i=head[x]; i; i=_[i].next)
#define Ms(a,b) memset((a),(b),sizeof(a))
#define endl '\n'

const LL maxn = 205;
const LL mod = 1e9+7;
LL f[maxn][maxn][maxn];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    #ifdef DEBUG
    freopen("data.txt","r",stdin);
    #endif
    LL t; cin>>t;
    while(t--) {
        LL n, m; cin>>n>>m;
        // string str; cin>>(str+1);
        char str[maxn]; cin>>(str+1);
        if(m&1) {
            cout<<0<<endl;
            continue;
        }
        Ms(f, 0);
        f[0][0][0] = 1;
        Fo(i,0,m-1) {
            Fo(j,0,i) { //left-right
                Fo(k,0,i) { //lcs
                	if(j) {
                		if(str[k+1]==')') f[i+1][j-1][k+1]=(f[i+1][j-1][k+1]+f[i][j][k])%mod;
                		else f[i+1][j-1][k] = (f[i+1][j-1][k]+f[i][j][k])%mod;
					}
					if(str[k+1]=='(') f[i+1][j+1][k+1] = (f[i+1][j+1][k+1]+f[i][j][k])%mod;
					else f[i+1][j+1][k] = (f[i+1][j+1][k]+f[i][j][k])%mod;
                }
            }
        }
        cout<<f[m][0][n]<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cls1277

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值