python 比特输出_用Python实现DFS的比特压缩器,python,蓝桥,杯,深度,搜索,之,BitCompressor...

python实现蓝桥杯深度搜索(DFS)之Bit Compressor

问题描述

问题描述

数据压缩的目的是为了减少存储和交换数据时出现的冗余。这增加了有效数据的比重并提高了传输速率。有一种压缩二进制串的方法是这样的:

将连续的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

题目解析

这道题就是要将压缩后的二进制进行还原,然后去查看还原后的二进制串是否唯一,如果唯一,就输出YES,如果不唯一就输出NOT UNIQUE,如果不存在,就输出NO。

思路

参数说明

nowIndex:正在处理的字符下标

getZero:当前获得的0的数量

getOne:已经检索到的1的数量

l:原串的长度

l_one:原串1的个数

lzero:原串0的个数

answer:标记答案是否唯一

temptZero:临时变量

temptOne:临时变量

initNum:存放第一组的参数

initString:存放压缩后的传,以列表的形式。

首先,我们拿案例输入的第一个案例来分析,这个题的第一个难点,我觉得应该是如果有一个1后面跟了很多的0的话,我们没有办法去判断到底怎么切割。以及,二进制怎么转换成十进制。第二个难点,就是分情况去讨论。现在我们先来说一下这个题目要分什么条件讨论。

第一个情况

首先,由于我们是用二进制去表示原来的1的长度,所以这里就会遇到一个问题,首先,如果原来是0110的话,其实我们可以不用压缩,因为压缩过后是0010这个长度和原来的长度并没有发生任何的改变,所以这个变形是没有意义的;然而,也会有这种情况,就是说,万一我们原来的二进制切片是01110,被压缩成了0110,这个也是有可能的。所以,第一个 情况就是将这两个现象区分开来,分别讨论

第二个情况

就是说,如果碰到010的话,我们是不用去压缩的,甚至不用动它,因为确实没有压缩的必要。

剪枝

当然,为了减少时间复杂度,我们还需要剪枝。

首先,如果说我们访问的当前字符下标如果越界了,就可以直接返回了。

然后就是说,如果answer的值大于1了,我们也直接返回,没有再讨论下去的必要了。

接着,如果我们接收到的0已经超过了原来的0的话,那么也没有必要讨论下去了。

最后就是,如果我们接收到的1也超过了原来的1的话,我们也没有必要再继续下去了。

基线条件(也就是递归的结束条件)

当我们访问的下标等于压缩后的长度的时候,注意,虽然我们下标试从0开始的,本来因该是到压缩后的长度减1,但是由于我们最后一次递归还是会给下标加一,所以这样就刚好等于原来的长度了。由于在python中初始化列表比较的麻烦,而且由于内部的花销平坦的内存管理机制,这里没有必要先初始化列表,然后一个个个的给它赋值,直接在列表的最后面加一个任意整数就行,这是为了防止 列表的下标越界而报错。

判断条件

当我们寻找到的1以及0的个数和原串是相等的话,我们就认为这个条件是符合的。由于我们是按顺序枚举,所以我们所讨论出来的情况也一定是原串的排列,或者是另一种展开方式。不会出现个数正确但是排列完全不对的情况。

如何二进制转十进制

由于我们是从左往右遍历,所以在二进制转十进制的时候,需要一些的变化。

temptOne = 2*temptOne

temptOne += int(initString[i])

这两行代码就是二进制转十进制。由于我们是从左往右移动,所以每移动一次,十进制的值就会乘以两倍。这就是第一行的意义。同时,由于0001,0010,这种是没有意义的,所以我们每次移动的时候一定是0011从左往右移动,或者0111或者1111,这种时候,我们就应该给0010+1然后向右移动,这就是第二行的大致意思。所以第二行要么加后一位的0,要么加后一位的1。这个处理方法还是很精妙的。

代码

def dfs(nowIndex,getZero,getOne):

"""

:param nowIndex: 当前正在处理的下标

:param getZero: 已经检索到的0的个数

:param getOne: 已经检索到的1的个数

:return: 直接返回,将函数结束

"""

global l,lzero,l_one,answer # 声明全局变量

'''下面是剪枝的条件'''

if answer > 1: return

if nowIndex > l: return

if getZero > lzero: return

if getOne > l_one : return

'''基线条件'''

if nowIndex == lcom:

if getZero == lzero and getOne == l_one:

answer += 1

return

'''开始正常的检索'''

temptZero = getZero

while initString[nowIndex] == '0':

temptZero += 1

nowIndex += 1

temptOne = 0

for i in range(nowIndex,lcom):

temptOne = 2*temptOne

temptOne += int(initString[i])

# 处理0110 展开

if temptOne == 2:

continue

if initString[i+1] == '1':

continue

next = i + 1

while initString[next]=='0':

next += 1

dfs(next,temptZero+next-i-1,getOne+temptOne)

if temptOne == 3:

dfs(next,temptZero+next-i-1,getOne+2)

if __name__ == '__main__':

# 第一次输入会有两个数,一个是原串的长度,还有一个就是原串1的个数

initNum = input().split()

l = int(initNum[0])

l_one = int(initNum[1])

lzero = l-l_one

# 第二次输入的是压缩后的串

initString = list(input())

lcom = len(initString)

initString.append(5) # 这个是让列表的长度变成17,这样防止列表的下标越界。

# 然后就是标识串是否唯一的变量: answer

answer = 0

dfs(0,0,0)

# 判断是否唯一

if answer == 1:

print("YES")

elif answer >=2:

print("NOT UNIQUE")

else:

print("NO")

参考博客

https://blog.csdn.net/weixin_43856851/article/details/104933960

https://blog.csdn.net/Raymond_YP/article/details/104450936

https://blog.csdn.net/yi_qing_z/article/details/88084875

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值