AtCoder - arc058_d Iroha Loves Strings解答与注意事项

链接:Iroha Loves Strings - AtCoder arc058_d - Virtual Judge

利用bitset这一数据结构,定义bitset类型的变量dpi表示第i到n个字符串能拼成的字符串长度都有哪些,比如00100101,表示能拼成的长度有0,2,5,(注意:bitset中的元素从右往左看,下标从0开始),利用递推关系更新dpi如下

dpi = dpi+1 | (dpi+1 << si的长度)

若dpik = 1,说明第i到第n个字符串可以拼成长度为k的字符串

之后模拟取数,i从1到k进行遍历,每次取当前能取的最小的字符,具体实现方式如下:(以下描述建议结合代码看)

定义候选对数组 pair<int,int> selij,i为0或1(用作开关变量),j是候选对象的编号,从1到cnt或ncnt,每个selij中存的是

<候选字符所在的字符串的序号,候选字符在该字符串中的下标>

第一个字符的候选字符串i,要满足dpi+1k-s\[i的长度] = 1,也就是选了第i个字符串后,之后的字符串必须保证能拼成(k-si的长度)的长度的字符串,将pair<i,0>加入sel

i从1到k遍历,每次在所有候选字符中选择字典序最小的那个字符作为答案,接着分情况讨论:

1.如果字符串还没用完,就继续将该字符串的下一个元素加入候选队列,如果有这样的字符串:

ab和ac,那么b和c都应该加入候选队列,

2.如果有一个字符串用完了,可能出现这样的情况:abc abc,这时应该先选前面的"abc",因为选完编号为i的字符串后,新入选的字符串的下标从 i+1 开始,如果选了第二个或后面的,就会有字符串没法被用到,从而导致丢失解或错误解,新入选的字符串(编号为 j )要满足dp j+1 k-i-新字符串的长度 等于1,这样选其能确保最终构成的字符串有可能长度为k,将pair<新字符串的编号,0>加入sel中

复杂度估计:nk

注意:

bitset和pair定义的先后会影响程序运行对错,正确的顺序是先定义bitset后定义pair,原因涉及到C++语言本身

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

const int maxn=2005,maxk=1e4+5;
char s[maxn][maxk];
int len[maxn];
int n,k;
bitset<maxk> dp[maxn];
pair<int,int> sel[2][maxn];


//pair和bitset的先后问题

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    //freopen("D:\\in.txt","r",stdin);

    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>s[i];
        len[i]=strlen(s[i]);
    }

    dp[n+1][0]=1;
    for(int i=n;i>=1;i--){
        dp[i]=(dp[i+1] | (dp[i+1]<<len[i]));
    }

    int cnt=0;
    //挑出第一个字符的可选对象
    for(int i=1;i<=n;i++){
        if(dp[i+1][k-len[i]]){
            //printf("%d可选\n",i);
            sel[0][++cnt]={i,0};
        }
    }

    int t=1;
    for(int i=1;i<=k;i++){
        int ncnt=0;
        t^=1;
        char cur='z';
        for(int j=1;j<=cnt;j++){
            cur=min(cur,s[sel[t][j].first][sel[t][j].second]);
        }
        cout<<cur;
        //更新下一组候选对象
        int minid=n+1;
        for(int j=1;j<=cnt;j++){
            int id=sel[t][j].first,pos=sel[t][j].second;
            if(s[id][pos]!=cur) continue;
            if(pos<len[id]-1){
                sel[t^1][++ncnt]={id,pos+1};
            }else{
                minid=min(minid,id);       //选序号最小的,这样后面的字符串还能用得上(不然会白白浪费字符串)
            }
        }
        for(int j=minid+1;j<=n;j++){
            if(k-i-len[j]>=0 && dp[j+1][k-i-len[j]]){
                sel[t^1][++ncnt]={j,0};
            }
        }

        cnt=ncnt;
    }

    return 0;
}
相关推荐
霍霍的袁15 分钟前
【C++11】左值右值引用与移动语义详细笔记(左值右值、右值引用、移动构造、移动赋值、引用折叠、万能转发)
c++·笔记·学习·visual studio
布莱克60518 分钟前
C++ 中 unordered_set 与 unordered_map 有什么不同?一篇讲透
开发语言·c++·哈希
小豆庄21 分钟前
视频脱敏引擎的追踪难题:转场误判定、ID撞号和淡入窗口
qt·算法·目标检测·音视频·视频·敏捷流程
海清河晏11128 分钟前
加油站问题
数据结构·算法·leetcode
GG.WinGel41 分钟前
质数数目(蓝桥杯练习题)
java·数据结构·算法·青少年编程·蓝桥杯
艾iYYY1 小时前
进程的状态
算法
秋天的怀念_David1 小时前
算法一千零一夜 · 第十七夜 长街点灯
java·程序人生·算法
鶴哥只手遮天1 小时前
AFSIM 2.9.0 深度解析:架构、原理与工程实践-第 1 章 项目全景与代码地图
c++
CV爱数码1 小时前
论文精读|RED-CNN:残差编解码网络,低剂量 CT 去噪的经典基线
网络·人工智能·算法·cnn·ct·重建·x-ray
充值内卷1 小时前
C++单文件迷宫游戏:TauriCPP 开发全记录
开发语言·c++·游戏