限制公共子序列计数 [动态规划]

限 制 公 共 子 序 列 计 数 限制公共子序列计数


正 解 部 分 \color{red}{正解部分}

F [ i , j ] F[i, j] F[i,j] 表示 S S S 序列前 i i i 项, T T T 序列前 j j j 项, i i i j j j 共同作为 公共子序列 末尾的方案数,
首先有一个很显然的状态转移方程,

F [ i , j ] = [ S i = = T j ] × ( ∑ k = 0 i − 1 ∑ p = 0 j − 1 F [ k , p ] ) F[i, j] = [S_i==T_j] \times \left(\sum\limits_{k=0}^{i-1}\sum\limits_{p=0}^{j-1} F[k, p]\right) F[i,j]=[Si==Tj]×(k=0i1p=0j1F[k,p]) ,时间复杂度 O ( N 4 ) O(N^4) O(N4),

对于这种 S i = T j S_i = T_j Si=Tj 时才生效的状态, 可以在 S i ≠ T j S_i \not = T_j Si=Tj 时, 直接继承, 所以不妨直接省掉一维,

从小到大 枚举 i i i, 设 F [ j ] F[j] F[j] 表示以 T j , S i T_j, S_i Tj,Si 这个 结尾, 与 S S S i i i 项构成 公共子序列 的方案数量,

F [ j ] = ∑ k = 0 j − 1 F [ k ] F[j] = \sum_{k=0}^{j-1} F[k] F[j]=k=0j1F[k], 意为 以 T k T_k Tk 这个 为末尾 转移到 以 ( T j = S i ) (T_j=S_i) (Tj=Si) 这个 为末尾,

  • 可以发现 S i S_i Si 在枚举 j j j 时是不变的, 因此可以方便的维护限制条件,
  • 随着 j j j 从小到大 枚举, 在枚举时不断使用当前 F [ j ] F[j] F[j] 完善决策集合即可实现后面 O ( 1 ) O(1) O(1) 转移 .

时间复杂度 O ( N 2 ) O(N^2) O(N2) .


实 现 部 分 \color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 3005;
const int mod = 1e9 + 7;

int N;
int M;
int K;
int Len;
int Ans;
int num0;
int S[maxn];
int T[maxn];
int B[maxn<<1];
int F[maxn<<1];

bool is[maxn<<1][maxn<<1];

int main(){
        N = read(), M = read(), K = read();
        for(reg int i = 1; i <= N; i ++) B[++ Len] = S[i] = read();
        for(reg int i = 1; i <= M; i ++) B[++ Len] = T[i] = read();
        std::sort(B+1, B+Len+1);
        Len = std::unique(B+1, B+Len+1) - B-1;
        for(reg int i = 1; i <= N; i ++) S[i] = std::lower_bound(B+1, B+Len+1, S[i])-B;
        for(reg int i = 1; i <= M; i ++) T[i] = std::lower_bound(B+1, B+Len+1, T[i])-B;
        for(reg int i = 1; i <= K; i ++){
                int s = read(), t = read();
                int p = std::lower_bound(B+1, B+Len+1, s)-B;
                if(p == Len+1 || B[p] != s) continue ; s = p;
                p = std::lower_bound(B+1, B+Len+1, t)-B;
                if(p == Len+1 || B[p] != t) continue ; t = p;
                if(is[s][t]) continue ; is[s][t] = 1;
        }
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = 1, s = 1; j <= M; j ++){
                        int ts = s;
                        if(is[T[j]][S[i]]) s = (s + F[j]) % mod; // 可以转移到后面.
                        if(S[i] != T[j]) continue ;
                        Ans = (Ans + ts) % mod; F[j] = (F[j] + ts) % mod;
                }
        printf("%d\n", Ans);
        return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值