TZOJ 8940 公共子序列

描述:

给定两个序列a1, a2, ..., an和b1, b2, ..., bm,求公共子序列的对数。

子序列是指从序列中删除任意多个元素后剩下元素按照先后顺序构成的子序列。

两个子序列相同是指所有元素值依次相同。

输入:

第一行为n和m(1≤n, m≤2000)。

第二行为a1, a2, ..., an。

第三行为b1, b2, ..., bm。

1≤ai≤1e5

输出:

输出答案 mod 1e9+7

题意:给定两个序列,求公共子序列的对数。(值相同但位置不同的子序列算作新的一对)

大致思路:使用dp求解,让dp[i][j]表示a数组的前i个数和b数组的前j个数中公共子序列的对数,然后列状态转移方程;

当a[i]!=b[j]时,dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1]。因为此时公共子序列的对数没有增加,dp[i][j]仍然是之前的相同子序列的对数。而dp[i - 1][j]表示的是 跟b[j]相关的公共子序列的对数+a数组的前i-1个数和b数组的前j-1个数中公共子序列的对数,dp[i][j-1]表示的是 跟a[i]相关的公共子序列的对数+a数组的前i-1个数和b数组的前j-1个数中公共子序列的对数。所以要把dp[i-1][j-1]减掉来去重。

当a[i] == b[j]时,dp[i][j]要在前面的基础上加上dp[i - 1][j - 1] + 1。因为同时跟a[i]和b[j]相关公共子序列的对数增加了,分别是自己本身的一对和前面不用上a[i],b[j]的dp[i-1][j-1]对。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

void IOS()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
}

const ll mod = 1e9 + 7;
int n, m;
int a[2007], b[2007];
ll dp[2007][2007]; 

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int j = 1; j <= m; j++) cin >> b[j];
   
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
 
            dp[i][j] = (dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mod) % mod;
            if (a[i] == b[j])dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] + 1) % mod;
        }
    }
    cout << (dp[n][m] + 1) % mod;//最后加上空序列

signed main()
{
    IOS();
    int T = 1;
    // cin >> T;

    while (T--)
    {
        solve();
    }

    return 0;
}

/*
4 4
1 2 3 4
1 3 2 4
12


2 2
1 1
1 1
6
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值