unlucky number
-
描述
-
我们定义在区间【l,r】之间只出现1和7组合的数是unlucky number,例如:1 、7、 11、17 都是unlucky numbers ,而 13 、27则不是,问在区间【l,r】内有多少 unlucky numbers??
-
输入
-
有多组测试数据(不超过100组)
每组输入两个整数l,r( 0 =< l<= r <= 10^18)
输出
- 每行输入一个结果 样例输入
-
1 7
样例输出
-
2
-
有多组测试数据(不超过100组)
假设有两个数组a[n]和b[n],a[i]表示i位数中共有多少种1和7的组合,我们看看怎么求a[i],首先看第一位,有两种选择(1和7),再看第二位,有两种选择,依次进行,每一位都是两种选择,根据分步乘法计数原理,a[i]=2^i。
b[i]表示1~10^i之间共有多少种1和7的组合,显然b[i]=a[1]+a[2]+a[3]+……a[i]=2^(i+1)-2,即b[i]=(b[i-1]+2)*2-2。
如果计算l~r之间的1和7的组合数,假设某函数f(n)可以计算1~n之间的组合数,则结果等于f(r)-f(l-1),但是如果l是一个字符串的大数的话,还要计算大数减法,为了避免这种情况,可以计算f(r)-f(l),然后如果l是1和7的组合的话,再加上1就行了,简单了许多。
那么如何计算任意一个数的符合条件的数的数目,比如8817,首先在1~1000之内有b[3]个数,然后当千位分别是1和7时,都有a[3]个数,所以是2*a[3],所以总共的情况有b[3]+2*a[3]=30,但是只有这么简单吗,是的,在这个情况下,他只需要O(1)的复杂度,但对于最坏的情况,比如7720,首先在1~1000之内有b[3]个数,然后当千位分别是1时,有a[3]个数,所以是a[3],当千位是7时,他的具体数目要往后面看,看百位是7,当是1时,有了a[2]种可能,当成为7时,还要往后看,十位是2,此时当十位是1时,包含了所有情况,即只有a[1],即所有的可能为b[3]+a[3]+a[2]+a[1]=28。基于此思想,可以形成最坏O(N)复杂度的算法,其中n为字符串的长度,所以此计数不只是局限于long long 内,以下是代码,仅供参考
#include <cstdio>
#include <cstring>
int dp[24];
int cau(char *s){
int len=strlen(s),sum,num,flag;
sum=dp[len-1];flag=1;
for(int i=0;i<len-1;i++){
num=s[i]-'0';
if(num==0) break;
if(num==1) {if(i==len-2) flag=0;continue;}
if(num<7) {sum+=dp[len-1-i]-dp[len-2-i];break;}
if(num==7) {sum+=dp[len-1-i]-dp[len-2-i];if(i==len-2) flag=0;continue;}
sum+=dp[len-1-i]-dp[len-2-i]+dp[len-1-i]-dp[len-2-i];break;
}
if(flag&&len>1) return sum;
num=s[len-1]-'0';
if(num<1) return sum;
if(num<7) return sum+1;
return sum+2;
}
char h1[24],h2[24];
int main(){
dp[1]=2;dp[2]=6;
for(int i=3;i<=24;i++) dp[i]=(dp[i-1]+2)*2-2;
while(scanf("%s%s",h1,h2)!=EOF){
int j=1,len=strlen(h1);
for(int i=0;i<len;i++){
if(h1[i]=='1'||h1[i]=='7') continue;
j=0;break;
}
printf("%d\n",cau(h2)-cau(h1)+j);
}
return 0;
}
以上代码中,dp数组就是代表b数组,h2和h1字符串分别表示r和l这两个数。cau()就是以上说的f()函数。
这个代码还可以再写短一点,让它更美观,不过这不是问题的主要矛盾,还有一个问题,这个cau函数真的能计算任意长度的数字吗,其实,最好的情况下,在计算cau(n)时,当n=10^31时,就会有2^32-2种可能,超出了int的范围,就算把cau()的返回值设为long long,那么n=10^63时,会超出范围,所以这个代码是不完美的,但可以修正,只要把cau()函数的返回值改为字符串,其中的加法改为大数加法就行了,就可以支持任意有限位的整数。