原题地址 。
— 第 2 天:礼品店 —
你进入內部,乘电梯到达它的另一个也是唯一一个停靠点:礼品店。"感谢您访问北极!"附近的一个标志欢快地呼喊着。你不确定谁甚至被允许访问北极,但你知道你可以从这里进入大厅,并从那里进入北极基地的其余部分。
当你浏览这令人惊讶地种类繁多的商品时,一位店员认出了你并请求你的帮助。
原来,一位年轻的精灵在礼品店的电脑上玩耍,结果给他们的礼品店数据库添加了一大堆无效的产品ID!对你来说,为他们识别出这些无效的产品ID肯定没问题,对吧?
他们甚至已经检查了大部分产品ID范围;只剩下几个产品ID范围(你的谜题输入)需要你检查。例如:
11-22,95-115,998-1012,1188511880-1188511890,222220-222224,
1698522-1698528,446443-446449,38593856-38593862,565653-565659,
824824821-824824827,2121212118-2121212124
(ID 范围在这里换行是为了清晰;在你的输入中,它们出现在一行很长的文本上。)
范围之间用逗号(,)分隔;每个范围给出其第一个ID和最后一个ID,中间用短横线(-)分隔。
由于那个小精灵只是在玩愚蠢的模式,你可以通过寻找那些仅由某个数字序列重复两次构成的ID来找到无效ID。因此,55(5 重复两次)、6464(64 重复两次)和 123123(123 重复两次)都将是无效ID。
这些数字都没有前导零;0101 根本不是一个ID。(101 是一个你需要忽略的有效ID。)
你的工作是找出给定范围内出现的所有无效ID。在上面的例子中:
11-22有两个无效ID:11和22。95-115有一个无效ID:99。998-1012有一个无效ID:1010。1188511880-1188511890有一个无效ID:1188511885。222220-222224有一个无效ID:222222。1698522-1698528不包含无效ID。446443-446449有一个无效ID:446446。38593856-38593862有一个无效ID:38593859。- 其余范围不包含无效ID。
将此示例中的所有无效ID相加,得到 1227775554。
如果将所有的无效ID加起来,你会得到什么?
第一部分比较简单,出现两次的ID是11、101、1001等的倍数,由于要求出无效ID,为了避免复杂的数学运算,根据输入的特点,用穷举硬除的方法。注意整除后的商的位数要比除数少一位,比如99/11=9,9比11少1位,符合要求;而110/11=10,10和11位数相等,不符合要求。
with recursive t as(select '11-22,95-115,998-1012,1188511880-1188511890,222220-222224,
1698522-1698528,446443-446449,38593856-38593862,565653-565659,
824824821-824824827,2121212118-2121212124' t),
b as(select row_number()over()rn,substr(b,1,instr(b,'-')-1)::bigint l,substr(b,instr(b,'-')+1)::bigint r
from(select unnest(string_split(replace(t,chr(10),''),','))b from t)) ,
c as(select b.*, (power(10,length(l::text)//2)+1)::bigint d,(power(10,length(r::text)//2)+1)::bigint d2,
(select count(*) from range(l,r+1)t(i) where i%d =0 and length((i//d)::text)=length(d::text)-1
or i%d2 =0 and length((i//d2)::text)=length(d2::text)-1)cnt,
(select sum(i) from range(l,r+1)t(i) where i%d =0 and length((i//d)::text)=length(d::text)-1
or i%d2 =0 and length((i//d2)::text)=length(d2::text)-1)sumi
from b
)
select sum(sumi)from c;
第二部分,除了上述倍数,还包括111、1111、10101、11111、1010101、101010101等的倍数,首先要把这些除数找出来。从输入数据可见,除数最大是10位,用repeat函数比较方便。整除后的商要和重复部分位数相同。
with recursive t as(select '11-22,95-115,998-1012,1188511880-1188511890,222220-222224,
1698522-1698528,446443-446449,38593856-38593862,565653-565659,
824824821-824824827,2121212118-2121212124' t),
b as(select row_number()over()rn,substr(b,1,instr(b,'-')-1)::bigint l,substr(b,instr(b,'-')+1)::bigint r
from(select unnest(string_split(replace(t,chr(10),''),','))b from t)) ,
c as(select k,j,('1'||repeat(substr('00001',j),k))::bigint a from range(1,11)t(k),range(1,6)t(j) where k*(6-j)<10),
c2 as(select b.*,(select count(*) from range(l,r+1)t(i),c where i%a =0 and length((i//a)::text)=6-j)cnt,
(select sum(distinct i) from range(l,r+1)t(i),c where i%a =0 and length((i//a)::text)=6-j)sumi
from b
)
--from c2 order by rn;
select sum(sumi)from c2;
先用子查询c构造10位以内的所有除数,共19种
select k,j,('1'||repeat(substr('00001',j),k))::bigint a from range(1,11)t(k),range(1,6)t(j) where k*(6-j)<10;
┌───────┬───────┬────────────┐
│ k │ j │ a │
│ int64 │ int64 │ int64 │
├───────┼───────┼────────────┤
│ 1 │ 1 │ 100001 │
│ 1 │ 2 │ 10001 │
│ 2 │ 2 │ 100010001 │
│ 1 │ 3 │ 1001 │
│ 2 │ 3 │ 1001001 │
│ 3 │ 3 │ 1001001001 │
│ 1 │ 4 │ 101 │
│ 2 │ 4 │ 10101 │
│ 3 │ 4 │ 1010101 │
│ 4 │ 4 │ 101010101 │
│ 1 │ 5 │ 11 │
│ 2 │ 5 │ 111 │
│ 3 │ 5 │ 1111 │
│ 4 │ 5 │ 11111 │
│ 5 │ 5 │ 111111 │
│ 6 │ 5 │ 1111111 │
│ 7 │ 5 │ 11111111 │
│ 8 │ 5 │ 111111111 │
│ 9 │ 5 │ 1111111111 │
├───────┴───────┴────────────┤
│ 19 rows 3 columns │
└────────────────────────────┘
然后逐个试除,注意笛卡尔积会产生重复的被除数,比如222222,既能被看做2210101,也能被看做2221001,还能看做2*111111,需要用distinct去重。

706

被折叠的 条评论
为什么被折叠?



