题目解析
传入一个数据n,要我们找出[0, n]范围内所有转换成的二进制中没有连续1的字符串的数的总数。
(好家伙,翻译得比原题还不说人话)
比如我传入一个5,从0到5我们写出所有的二进制:
0、1、10、11、100、101
其中11是有联系的1,我们不要,剩下的五个数都是符合范围的。
看数据的范围,直接10* *9,根本不可能去遍历,转二进制然后找,所以我们要总结一些规律,这里我想到的是按照位数。
1位数:0、1;
2位数:10、11(这个不要);
3位数:100、101;
四位数:1000、1001、1010
…………
貌似也没什么规律吧。。
但是我们看这个:(六位数的处理)
不知道能不能看懂,我们来分析一下。
主要思想:1后面只能跟0。
最开始的两位一定是10,(6位数,所以第一位是1,然后根据思想,第二位是0),
剩下的我们只能进行测试了,感觉第三位可以放一个1,也可以放0。
如果放1的话,第四位只能放0了。
等等,这一幕怎么有一点眼熟?
我们在处理6的时候,不也是这样的吗!
那这个是谁?
是4!
然后第三位放0,我们第四位又可以选择,放1此时就是一个3,放0我们继续划分,最终结果:
距离最后有x位,那么这个数就是x。
所以我们就可以把dp[1:n-2]全部加起来,得到二级制为n位的总数。
再转化一下,我们将dp[i] += dp[i-1],将dp[i]处理成从0到2i-1的数量。
然后是下一个问题,我们怎么处理" 10101001111’b "这样一个数据?
首先这个是一个11位二进制,我们先加一个dp[10],也就是从0到210-1,
然后剩下的范围是210到10101001111’b,我们可以确定:这些数都是11位二进制,并且前两位都是10。
那么好了,我们加一个dp[8],dp[8]是从0到28-1,但是别忘了我们还有前两位的,所以就是10_000000000-10_011111111,刚好能和前面的数拼接上,同理,我们还可以拼接dp[6]、dp[3],到了这里我们停一下。
是不是准备在dp[3]后面接一个dp[2]了?
确实是需要添加的,因为我们的dp[2]只能到’11’,但再往下,就会出现两个1相邻的情况。
(这里的实现是我们是检测当前index的前一位,如果都是1那么就退出)
最后一个问题,我们其实漏了一个数,没错就是本身,加一个判断即可。
代码:
class Solution:
def findIntegers(self, n: int) -> int:
# 因为我们后面数组长度的问题,所以是要做限制的
if n == 1:
return 2
elif n < 4:
return 3
elif n == 4:
return 4
# 长度这里要加一!
length = int(math.log(n, 2))+1
# 用长度先计算着,然后将剩下的位置选出来
dp = self.get_num(length)
#print(dp)
# 计算剩下的,我们找到每一个为1的位置,然后将其转化成一个子问题
s = bin(n)[2:]
#print(s)
# 注意可能是等于的情况是符合的,我们要添加上
ret = 0 if '11' in s else 1
for i in range(len(s)):
if s[i] == '1':
# 下表从0开始,i=4相当于是0-1111的全部值,所以我们前面要加上那个1
#print(ret)
ret += dp[i]
if i and s[i-1] == '1':
break
return ret
def get_num(self, length):
'''规律,我们5位的前两位一定是10,那么第三位是1,就是一个三位的问题
如果选0,再下一位可以是0也可以是1,那么此时1是2位的问题,再下面是一位的问题
那么dp[i] = sum(dp[0~i-2])'''
dp = [0]*max(length,4)
dp[1], dp[2], dp[3] = 2, 1, 2
# 这个计算的是i位的数据有几个符合条件的
for i in range(4, length):
dp[i] = dp[i-1]+dp[i-2]
# 这时我们还要再加一次,最后用的是前面所有的值
for i in range(1, length):
dp[i] += dp[i-1]
# 这个情况是对最后一位为0的
dp[0] = 1
# dp[i]就是从0到2**i-1的所有合适值
return dp[::-1]
这个是能跑过的,双80我记得是。
个人感觉和官方解答思想差不多,但我这个麻烦点不过好想?
区别就在他是预判了31位够了,直接计算,我是根据长度来计算的。
然后因为我dp启动要求长度最小为4,产生了一个很离谱的结构,昨天过了就没力气管了。
(秋赛是真的挺离谱的,对于我这种野生算法玩家)
代码优化
你说要是都看到这里了我再拿烂代码给人看,这好吗,这不好。
我整理了一下,时间从32ms到28ms,空间涨了一点。
class Solution:
def findIntegers(self, n: int):
# 长度我们需要加一,因为没法整除,即使整除了也要加一(这里想一下8是1000就知道了)
length = int(math.log(n, 2))+1
dp = [0]*32
dp[1], dp[2], dp[3] = 2, 1, 2
for i in range(4, 32):
dp[i] = dp[i-1]+dp[i-2]
for i in range(1,32):
dp[i] += dp[i-1]
# 针对最后一位为1的情况,比1小的还有一个0,前面的我们都没处理这个
# 也是在最后才发现的这个问题
dp[0] = 1
dp = dp[length-1::-1]
# 获取二进制字符串,并去掉前面的Ob
s = bin(n)[2:]
print(len(dp),len(s))
ret = 0 if '11' in s else 1
for i in range(len(s)):
if s[i]=='1':
ret += dp[i]
if i and s[i-1]=='1':
break
return ret
和答案的相比,还是没那么整洁,但是能更好的体现思路吧。