使用DuckDB SQL求解Advent of Code 2024第9题 磁盘碎片整理

原题地址 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,就非常慢了。

相关推荐
小南家的青蛙1 小时前
LeetCode面试题 04.06 后继者
算法·leetcode·职场和发展
Z***u6591 小时前
MySQL物联网开发
数据库·mysql
IT·小灰灰1 小时前
基于Python的机器学习/数据分析环境搭建完全指南
开发语言·人工智能·python·算法·机器学习·数据分析
Wang's Blog1 小时前
MongoDB小课堂: 容器化部署与操作综合指南
数据库·mongodb
IT小哥哥呀1 小时前
从零到NoSQL:一个动手的MongoDB教程(无需安装!)
数据库·mongodb·nosql·教程·初学者
J***79391 小时前
MySQL语音识别案例
数据库·mysql·语音识别
y***86691 小时前
MySQL语音识别开发
数据库·mysql·语音识别
武昌库里写JAVA2 小时前
Java如何快速入门?Java基础_Java入门
java·vue.js·spring boot·后端·sql
wefg12 小时前
【C++】智能指针
开发语言·c++·算法