- 不含连续1的非负整数
给定一个正整数 n,找出小于或等于 n 的非负整数中,其二进制表示不包含 连续的1 的个数。
示例 1:
输入: 5
输出: 5
解释:
下面是带有相应二进制表示的非负整数<= 5:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
其中,只有整数3违反规则(有两个连续的1),其他5个满足规则。
说明: 1 <= n <= 109
题解
我们先把问题简化,因为涉及二进制数,所以转换成二进制数来考虑,于是比如长度为len(x)+2的二进制数,那么不符合要求的二进制数有:10x和11x,10x这种情况的数目为x中不符合要求的数目,我们假设coun[x]表示二进制长度为x的数字不符合要求的数目,那么10x的答案为coun[x],11x的结果就简单了,直接为pow(2,len(x)),因为前面的11已经不符合要求,后面的x无论怎么取都是不对的,于是我们分别dp[n][0]和dp[n][1]表示长度为n的二进制数10x和11x的数目分别为多少。
于是某个数字拆成二进制数为:110101,可以看成是:
100000 -> 5个位置随便取值,错误数+coun[5]
10000 -> 4个位置随便取值,错误数+coun[4]
100 -> 2个位置随便取值,错误数+coun[2]
1 -> 0个位置随便取值,错误数+coun[2]
分别对应,然后又回到整体,110101前面的11是不符合要求的,导致后面的101都是错的,101对应了6个数字(000也是一种情况),所以错误数加上这个部分。
最后就是110101代表的数字+1减去错误数为答案。
AC代码
class Solution
{
public:
int dp[33][2];
int coun[33];
string int_to_str(int x)
{
vector<int>q;
while(x>0)
{
q.push_back(x%2);
x/=2;
}
string t="";
for(int i=q.size()-1;i>=0;i--)
t+=(q[i]+'0');
return t;
}
int findIntegers(int n)
{
memset(dp,0,sizeof(dp));
memset(coun,0,sizeof(coun));
dp[0][0]=dp[0][1]=0;
dp[1][0]=dp[1][1]=0;
dp[2][0]=0;
dp[2][1]=1;
coun[2]=1;
string num = int_to_str(n);
for(int i=3;i<=num.length();i++)
{
dp[i][0] = coun[i-2];
dp[i][1] = int(pow(2,i-2));
coun[i] = coun[i-1] + dp[i][0] + dp[i][1];
//cout<<i<<" "<<dp[i][0]<<" "<<dp[i][1]<<" "<<coun[i]<<endl;
}
//cout<<num<<endl;
int res = coun[num.length()-1];
for(int i=1;i<num.length();i++)
{
if(num[i]=='1')
res += coun[num.length()-i-1];
if(num[i]=='1'&&num[i-1]=='1')//特判
{
int sum=0;
for(int j=num.length()-1,k=0;j>i;j--,k++)
{
sum += (num[j]-'0')*int(pow(2,k));
}
sum++;//全0的情况
res += sum;
return n-res+1;
}
}
return n-res+1;
}
};