String Distance( 比较难的dp )

String Distance( 比较难的dp )

hdu 6774

题意:给两个字符串A和B( 只由小写字母组成 ),A的长度最大1e5,B的长度最大20 。q次询问(最多1e5次),每次询问给一个区间 [ l , r ] 1<=l,r<=lenA 问A[ l,l+1...r ] 和 B整个字符串 进行最少几次操作可以变成相同的,操作1:选择任意一个字符串在任意一个位置增加一个字符;操作2:选择任意一个字符串删除任意一个字符。

思路:显然操作1增加字符是没有用的,因为我增加字符就是为了让增加的字符和另外的一个字符相同,那我直接删掉另外的字符就可以了。这就转变成了求字符AB的最大公共子序列,把不是最大公共子序列的字符删掉就是答案,ans=lenA+lenB-2*LCS。

到这基本就能想到用dp来做了,dp的一个特点就是都是从小状态往大状态迁移,B字符串比较小,可能是dp的一个维度,考虑B字符串只有前1个,前两个,前三个,考虑有什么联系。我们是要求LCS的,想一下是把LCS放到左边维度里还是放到右边答案里,容易想到开个二维dp[ i ][ j ] 表示 B取前 i 个字符时和A的LCS是 j 时,需要的A的范围。

再考虑考虑A的边界条件,准确的表达,dp[ i ][ j ] 表示 B的前 i 字符,和A的lcs长度是 j ,在[l 条件下 的 r]

这里涉及到在[ l,左边界的条件下,还考虑到第一维度是B的长度,状态更新时涉及到单个字符。所以我们再引入g[ i ][ j ]表示 A[i..n] 里字符 j 最早出现的下标。

这样我们得到状态迁移方程是:dp[ i ][ j ] = min dp[ i ][ j ], dp[ i-1 ][ j ] , g[ dp[i-1][j-1 ][ B[ i ] ]

解释是:dp[ i-1 ][ j ] 是前i-1个字符lcs也是 j 那么加上当前的i 也还是满足lcs是 j

g[ dp[i-1][j-1 ][ B[ i ] ] 是前i-1个字符lcs是j-1 那么我当前的B[ i ] 一定要加入到lcs里才可以,就需要找在前 j-1个字符匹配成功的后面再匹配B[ i ] .

初始条件是:dp[i][0] = l-1. 

写代码过程中一定要注意在状态迁移时有没有越界情况,我就因为g数组卡的边界,导致dp迁移时g比边界大了一些,wa了半天。

上面只是思路,写代码过程中还有好多细节需要注意。

看了题解,还是写了三个小时才A的我实在是太菜了.....

代码:

#include <bits/stdc++.h>
#define int long long

using namespace std;

int dp[22][22]; /// B的前i字符,lcs长度是j ,在[l 条件下 的 r]
int g[200005][30];
string A,B;
int lenA,lenB;

void init()
{
    for ( int i=0; i<=180002; i++ ) {  // 在这全设置成最大值了,卡边界出错了
        for ( int j=0; j<=28; j++ ) {
            g[i][j] = lenA+1;
        }
    }
    for ( int i=lenA-1; i>=0; i-- ) {
        for ( int j='a'-'a'; j<='z'-'a'; j++ ) {
            if ( A[i]-'a'==j ) g[i][j] = i;
            else g[i][j]=g[i+1][j];
        }
    }
}

signed main()
{
//    freopen("1012.in","r",stdin);
//    freopen("1012.out","w",stdout);
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T;cin>>T;
    while ( T-- ) {
        cin>>A>>B;
        lenA = A.size();lenB = B.size();
        init();
        int m;cin>>m;
        while ( m-- ) {
            int l,r;cin>>l>>r;
            for ( int i=0; i<=lenB; i++ ) {
                for ( int j=0; j<=lenB; j++ ) {
                    dp[i][j] = r;
                }
            }
            /
            for ( int i=0; i<=lenB; i++ ) dp[i][0] = l-2;
            for ( int i=1; i<=lenB; i++ ) {
                for ( int j=1; j<=i; j++ ) {
                    dp[i][j] = min( dp[i][j],dp[i-1][j] );
                    dp[i][j] = min( dp[i][j],g[ dp[i-1][j-1]+1 ][ B[i-1]-'a' ] );
                }
            }
            /
            int isp = 0;
            for ( int i=lenB; i>=0; i-- ) {
                for ( int j=1; j<=lenB; j++ ) {
                    if ( dp[j][i]+1<=r ) {
                        isp = 1;
                        cout << r-l+1+lenB-2*i << endl;

                        break;
                    }
                }
                if ( isp==1 ) break;
            }
        }
    }

    return 0;
}

/*
1
rtedxofnuatdpogdzftepqhlwiavwisshpvlcwdkzlccofacdisafkpzmppgdwahposjlgqxqdupksokprgaymznbtyuenyikazrmjonfqawzcmuaqowrizdortxplnogmntadqqpwgolfcyqswtncqldgkrmgbovkyaxsuqwzftujheouhiynkjbzdnnhmreheoawkkljxmiwghewmqvjvmywukckjzwvfnjomthjkvijujdksawjmfrrgmhvpqazesdmeyjvougzmhlxrqmdyripgomcrawinxmfiyrxdhapolnjrveymwjfnhxrojwzbqyfijufsptrtoodwjdmticprauzscyorujojvwrrlpcvjwynvdcmdjwxruhpcandrefgtdvilsfhdnumdzqhcnzzqlevqhoeivtqvudorczqcvcvhlkfljcgi
bbbbabababbaabbababb
1
444 444


1
abcdwadefghiyuwehskwaspsduwxyz
wwac
5
1 6
2 9
7 12
4 19
12 23
6 8 10 16 10
*/

 


刚学了一晚上这个dp,今天打百度之星碰到了一道相似的dp,太爽了。

Problem Description

Alice 和 Bob 准备 solo 一场算法竞赛。

比赛一共有 n 个题,编号为 1,2....,n,对于第 i 道题,Alice 需要 a[i] 分钟写出一份正确的代码,Bob 需要 b[i] 分钟写出一份正确的代码。

比赛规则为

  1. 每道题第一个通过的人积 1 分,如果两人同时 AC 该题,只有 Alice 得分。
  2. 比赛时长为 10 ^ {18} 分钟。

Alice 和 Bob 的比赛策略都满足:决定要去做某道题后,会一直解决该题,直到自己或者对手 AC 此题,如果对手 AC 该题,则会立即放弃这题。

Bob 写完一份正确的代码后会立即提交,但 Alice 写完一份正确的代码,可以先暂时不交题,等之后再交(交题的时间忽略不计,任何时间都可以交题)。

另外 Alice 知道 Bob 是按 1,2,....,n 的顺序来依次做题,知道每道题自己需要的时间和 Bob 需要的时间(即 a 序列和 b 序列)。

输出 Alice 最优策略下最多得几分。

Alice 和 Bob 想题都不需要时间。

Input

第一行一个整数 t(1 <=t <= 10) 表示 t 组数据。

每组数据第一行一个整数 n (1 <= n <= 2000) 表示题数。

第二行 n 个整数,表示 a[1],a[2],...a[n](1 <= a[i] <= 1000000000) 。

第三行 n 个整数,表示 b[1],b[2],...b[n](1 <= b[i] <= 1000000000) 。

保证至多只有一组数据 n>100。

Output

对于每组数据,一行一个整数表示答案。

Sample Input

2
6
6 6 6 6 6 6
1 1 1 1 1 1
3
1 2 3
5 1 1

Sample Output

Copy

1
3

样例解释
Case 1 开场直接 rush 最后一题。
Case 2 [0,1) 写掉第一题,第 5 分钟交;[1,3) 写第二题第 6 分钟交,[3,6) 写第三题第 6 分钟交。
#include <bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f

using namespace std;

int dp[2005][2005];
int a[2005],sumb[2005],n;

signed main()
{
    // 前i个题中,得分为j,需要的时间
    // dp[i][j] = dp[i-1][j]
    // dp[i][j] = dp[i-1][j-1]+a[i] <= sumb[i]
    int Ti;cin>>Ti;
    for ( int ji=0; ji<Ti; ji++ ) {
        cin>>n;
        sumb[0] = 0;
        for ( int i=1; i<=n; i++ ) scanf("%lld",&a[i]);
        for ( int i=1; i<=n; i++ ) {
            int x;scanf("%lld",&x);
            sumb[i] = sumb[i-1]+x;
        }
        memset(dp,0x3f3f3f3f,sizeof(dp));
        for ( int i=0; i<=n; i++ ) dp[i][0] = 0;
        for ( int i=1; i<=n; i++ ) {
            for ( int j=0; j<=n; j++ ) {
                dp[i][j] = min( dp[i][j], dp[i-1][j] );
                if ( dp[i-1][j-1]+a[i]<=sumb[i] ) {
                    dp[i][j] = min( dp[i][j], dp[i-1][j-1]+a[i] );
                }
            }
        }
        int isp = 0;
        for ( int i=n; i>=0; i-- ) {
            for ( int j=n; j>=1; j-- ) {
                if ( dp[j][i]<=sumb[j] ) {
                    cout << i << endl;
                    isp = 1;
                    break;
                }
            }
            if ( isp==1 ) break;
        }
    }


    return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值