问题描述
数据压缩的目的是为了减少存储和交换数据时出现的冗余。这增加了有效数据的比重并提高了传输速率。有一种压缩二进制串的方法是这样的:
将连续的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")