字符串

1.1 Problem Statement

小 D 正在研究字符串。
小 D 已经对字符串的最长公共子序列问题非常熟悉了。如果你并不知道这是什
么,下面给出了这个问题的定义:
对于一个字符串 S = S1S2 · · · Sn,定义他的一个子序列是任意满足 1 ≤ i1 <
i2 < · · · < ik ≤ n 的字符串 Si1 Si2
· · · Sik。
对于两个字符串 S, T,我们定义他们的最长公共子序列为一个最长的字符串,
使得它既是 S 的子序列,又是 T 的子序列。
小 D 觉得最长公共子序列问题没有什么挑战,因此他决定研究这个问题的反方
向:最短公共非子序列。其中,字符串 S 的非子序列指不是 S 的子序列的字符串。
对于两个字符串 S, T,我们定义他们的最短公共非子序列为一个最短的字符串,
使得它既是 S 的非子序列,又是 T 的非子序列。
为了降低难度,小 D 决定只研究两个 0/1 字符串的最短公共非子序列。小 D 又
觉得这个问题过于简单了,因此他还想要求出这两个字符串字典序最小的最短公共非
子序列。其中,我们认为字符串 A = A1A2 · · · Ak 字典序小于字符串 B = B1B2 · · · Bk,
当且仅当存在某个 1 ≤ i ≤ k,使得 Ai < Bi 且 ∀1 ≤ j < i,有 Aj = Bj。
这下小 D 不会了,请你帮帮他。

1.2 Input Format
从标准输入读入数据。
第一行两个正整数 n, m,表示两个字符串 S, T 的长度。
第二行一个长度为 n 的,仅由 0/1 组成的字符串 S,表示第一个字符串。
第三行一个长度为 m 的,仅由 0/1 组成的字符串 T,表示第二个字符串。

1.3 Output Format
向标准输出输出答案。
输出一行一个字符串,表示字典序最小的,最短公共非子序列。

1.4 Sample 1
1.4.1 Input
4 7
0101
1100001

1.4.2 Output
0010

1.5 Sample 2
见下发文件 str/str2.in 与 str/str2.ans。
1.6 Sample 3
见下发文件 str/str3.in 与 str/str3.ans。

1.7 Constraints
对于所有测试数据,1 ≤ n, m ≤ 4000。
• 子任务 1(15 分):n, m ≤ 10;
• 子任务 2(20 分):m ≤ 10;
• 子任务 3(25 分):n, m ≤ 500;
• 子任务 4(40 分):无特殊限制。
题解:
首先,容易发现,对于判断一个字符串是否是原字符串的字串,一定是贪心地从头往后跳。所以我们需要的就是构造一个字符串,使得能让两个原字符串都用最短的步数跳完。这时假如再在末尾加一个字符,就可以满足。
所以考虑动态规划。
f[i][j]表示跳完i->n,j->m的最短步数,由贪心可得
f[i][j]=min( f[snx[i][0]][tnx[j][0]] , f[snx[i][1]][tnx[j][1]] )+1;
可以预处理snx[i][0],snx[i][1]
至于字典序可以转移时优先取0

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 4005
using namespace std;
int f[N][N],snx[N][2],tnx[N][2];
int n,m,ans[N][N];
char str[N];
int main(){
    cin>>n>>m;
    cin>>1+str;
    snx[n+1][1]=snx[n+1][0]=n+1;
    tnx[m+1][1]=tnx[m+1][0]=m+1;
    for(int x=n+1,y=n+1,i=n;i+1;--i){
        snx[i][1]=x;snx[i][0]=y;
        str[i]=='1'?x=i:y=i;
    }
    cin>>1+str;
    for(int x=m+1,y=m+1,i=m;i+1;--i){
        tnx[i][1]=x;tnx[i][0]=y;
        str[i]=='1'?x=i:y=i;
    }
    for(int i=n+1;i+1;--i){
        for(int j=m+1;j+1;--j){
            int fa=f[snx[i][0]][tnx[j][0]]+1,fb=f[snx[i][1]][tnx[j][1]]+1;
            if(fa<=fb)f[i][j]=fa,ans[i][j]=0;
            else f[i][j]=fb,ans[i][j]=1;
        }
    }
//  cout<<ans[0][0]<<endl;
    int x=0,y=0;
    while(!(x==n+1&&y==m+1)){
        int t=ans[x][y];
        char g=t+'0';
        cout<<g;
        x=snx[x][t];y=tnx[y][t];
    }
    cout<<endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值