原题地址 https://adventofcode.com/2024/day/9
第 9 天:磁盘碎片整理程序
再次按下按钮后,你们来到了某个友好端足类动物熟悉的走廊里!好在你们每个人都莫名其妙地拥有了一艘个人迷你潜水艇。历史学家们喷着水流去寻找首领,主要是通过直接撞向墙壁的方式。
在历史学家们快速弄清楚如何驾驶这些东西时,你注意到角落里有一只端足类动物正在费力地操作他的计算机。他试图通过压缩所有文件来制造更多连续的可用空间,但他的程序不工作;你主动提出帮忙。
他向你展示了他已经生成的磁盘映射(你的谜题输入)。例如:
2333133121414131402
该磁盘映射使用一种密集格式来表示磁盘上文件和可用空间的布局。数字交替表示文件的长度和可用空间的长度。
因此,像 12345 这样的磁盘映射将表示:一个 1 块的文件,两个块的可用空间,一个 3 块的文件,四个块的可用空间,然后是一个 5 块的文件。像 90909 这样的磁盘映射将表示三个连续的 9 块文件(它们之间没有可用空间)。
磁盘上的每个文件还有一个 ID 号,基于它们在重新排列前出现的顺序,从 ID 0 开始。所以,磁盘映射 12345 有三个文件:一个 ID 为 0 的 1 块文件,一个 ID 为 1 的 3 块文件,以及一个 ID 为 2 的 5 块文件。使用一个字符代表每个块,其中数字是文件 ID,. 是可用空间,磁盘映射 12345 表示这些单独的块:
0..111....22222
上面的第一个例子 2333133121414131402 表示这些单独的块:
00...111...2...333.44.5555.6666.777.888899
这只端足类动物希望将文件块一次一个地从磁盘末端移动到最左边的可用空间块(直到文件块之间没有剩余间隙)。对于磁盘映射 12345,该过程如下所示:
0..111....22222
02.111....2222.
022111....222..
0221112...22...
02211122..2....
022111222......
第一个例子需要更多步骤:
00...111...2...333.44.5555.6666.777.888899
009..111...2...333.44.5555.6666.777.88889.
0099.111...2...333.44.5555.6666.777.8888..
00998111...2...333.44.5555.6666.777.888...
009981118..2...333.44.5555.6666.777.88....
0099811188.2...333.44.5555.6666.777.8.....
009981118882...333.44.5555.6666.777.......
0099811188827..333.44.5555.6666.77........
00998111888277.333.44.5555.6666.7.........
009981118882777333.44.5555.6666...........
009981118882777333644.5555.666............
00998111888277733364465555.66.............
0099811188827773336446555566..............
这个文件压缩过程的最后一步是更新文件系统校验和。要计算校验和,需将每个块的位置与其包含的文件 ID 号相乘的结果相加。最左边的块位置为 0。如果块包含可用空间,则跳过它。
继续第一个例子,前几个块的位置乘以其文件 ID 号为:0 * 0 = 0, 1 * 0 = 0, 2 * 9 = 18, 3 * 9 = 27, 4 * 8 = 32,依此类推。在这个例子中,校验和是这些值的总和,即 1928。
使用他请求的过程来压缩端足类动物的硬盘。产生的文件系统校验和是多少?(复制/粘贴此谜题的输入时要小心;它是一个单独的、非常长的行。)
我的代码
sql
with recursive t as
(select array_to_string([ repeat(case i%2 when 0 then chr((48+i//2)::int) else '.' end, substr(a, i+1, 1)::int) for i in range(0, length(a)-1)], '')s
from(select trim(content) a from read_text('2409-input0.txt') )),
--from (select '2333133121414131402' a )),
v as(select s, replace(s, '.', '') n, reverse(n) r, length(s)-length(n) va , [ i for i in range(length(s), 0, -1) if s[i:i]<>'.'] pos from t),
a using key(o)as (select 0 o, 1 lv, s, instr(s, '.') i, r, va,pos from v
union
select 0, lv+1 , rpad(substr(s, 1, i-1) ||substr(r, lv, 1) || s[ i+1:pos[lv]-1 ] ,length(s)::int,'.') s , instr(s[i+1:pos[lv]], '.')+i i ,r,va,pos from a where lv<va and
i=instr(s, '.')
)
select any_value(s), sum(n*(ascii(substr(s,n+1,1))-48))chksum from a, range(0, instr(s, '.')-1)b(n) ;
一开始没考虑ID大于9的情况,后来意识到了,就用chr函数把ID转成utf8字符,然后计算校验和再用ascii函数转回来。
解决小字符串时正确,但是测试输入文件有20K,就非常慢了。