Bit Compressor(DFS搜索)

题目:

资源限制
时间限制:1.0s 内存限制:256.0MB

问题描述
  数据压缩的目的是为了减少存储和交换数据时出现的冗余。这增加了有效数据的比重并提高了传输速率。有一种压缩二进制串的方法是这样的:
  将连续的n个1替换为n的二进制表示(注:替换发生当且仅当这种替换减少了二进制串的总长度)
  (译者注:连续的n个1的左右必须是0或者是串的开头、结尾)
  比如:11111111001001111111111111110011会被压缩成10000010011110011。原串长为32,被压缩后串长为17.
  这种方法的弊端在于,有时候解压缩算法会得到不止一个可能的原串,使得我们无法确定原串究竟是什么。请你写一个程序来判定我们能否利用压缩后的信息来确定原串。给出原串长L,原串中1的个数N,以及压缩后的串。
  L<=16 Kbytes,压缩后的串长度<=40 bits。

输入格式
  第一行两个整数L,N,含义同问题描述
  第二行一个二进制串,表示压缩后的串

输出格式
  输出"YES"或"NO"或"NOT UNIQUE"(不包含引号)
  分别表示:
  YES:原串唯一
  NO:原串不存在
  NOT UNIQUE:原串存在但不唯一

样例输入
样例1:
32 26
10000010011110011
样例2:
9 7
1010101
样例3:
14 14
111111

样例输出
样例1:YES
样例2:NOT UNIQUE
样例3:NO

分析:

  • 新、原串易搞混,这里用长串、短串表示。题目即意为:已知短串,长串长L(L<1610248=131072位),长串含1的个数N(N<40位),求长串是否唯一。我设定长串通过“编码”得到短串,短串通过“译码”得到长串。这里的编码方式就是十进制转二进制,反之,译码方式就是二进制转十进制。
  • 通过DFS搜索短串,还原成长串。搜索原则是译码成1的个数由少到多。
  • 必须以1开头,如果有连续的1,必须在一起译,即译码的结尾不能是1.
  • 010只能译码成1,0110既可以译码成11,也可以译码成111,之后都是一一对应

代码:

#include <iostream>
#include <string.h>

using namespace std;

int L,N,l;//L为长串长度,N为长串1的个数,l为短串长度
char a[50];//存储短串(从1开始)
int flag=0;//成功匹配的次数,0代表失败,2及以上代表歧义

void dfs(int num_1, int len, int i)//num_1为目前译码的1的数量,len为目前译码得到的长码长度,i为需要译码的起始位置
{
    if(i==l)//当短码最后一位为译码开始的地方
    {
        dfs(num_1+a[i]-'0',len+1,i+1);
        return;
    }
    if(i>l)//一轮译码结束
    {
        if(len==L && num_1==N)
        {
            flag++;
        }
        return;
    }
    if(len>L || num_1>N || flag>=2) return;//显然如果发生以上三种情况,可以退出
    if(a[i]=='0')//0的话只要继续下一步译码,num_1不变
    {
        dfs(num_1,len+1,i+1);
        return;
    }
    //下面讨论a[i]==1的情况
    if(a[i-1]=='1') return;//和0110、010匹配,因为之前已经算过了
    long long temp=0;//计算i~j这一块二进制几个1,虽然题目中最多16*1024*8=131072位,在int范围内,但在译码的时候可能超过
    if(a[i+1]=='0')//类似010
    {
        dfs(num_1+1,len+1,i+1);
        //不写return是因为如果单独译码不行,还要继续连着后面译码,例如10100可以译成101111和1111100,因此还需要参加for循环
    }
    if(a[i+1]=='1' && a[i+2]!='1')//类似0110,这里不用=='0'是因为可能溢出
    {
        dfs(num_1+2,len+2,i+2);
    }
    //三或三个以上的1同时出现,或者0110解释成11不能满足条件
    for(int j=i;j<=l;j++)
    {
        temp*=2;//左移一位
        temp+=a[j]-'0';//译码完成
        if(temp+num_1>N || temp+len>L) break;//失败
        if(temp>j-i+1 && a[j+1]!='1')//省略了010的重复搜索
            dfs(num_1+temp,len+temp,j+1);//i~j的一次译码,a[j+1]为0或溢出
    }
}


int main()
{
    cin>>L>>N;
    cin>>(a+1);
    l=strlen(a+1);
    dfs(0,0,1);
    if(flag==1) cout<<"YES"<<endl;
    if(!flag) cout<<"NO"<<endl;
    if(flag>=2) cout<<"NOT UNIQUE"<<endl;
    return 0;
}

总结:

有关DFS搜索:本题的搜索较复杂,需要讨论的部分比较多,建议先挑明显的先写掉,比如结束条件。之后的关于010、0110的特别搜索,要反复斟酌。

改写自链接:https://blog.csdn.net/Yi_Qing_Z/article/details/88084875

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值