AdventOfCode 2025 Day 3

Part One

987654321111111

从一个较长的字符串中,挑选两个数字,不改变顺序,组成最大的两位数,比如这里的98

可以先求出十位数的数字,再求出个位数的数字

  • 十位数,就是未末尾的最大最靠前的数字
  • 个位数,就是排在十位数后面的最大数字
with origin as (
    SELECT row_number() over () as rn, line, length(line) as len FROM lance_input
), splits as (
    SELECT rn, len, x.pos :: integer as num, idx
    FROM origin, regexp_split_to_table(line, '') with ordinality as x(pos, idx)
), first_num AS (
    SELECT rn, row_number() over (partition by rn order by case when idx != len then num else -1 end desc, idx) as first_rk, num, idx
    FROM splits
), second_num AS (
    SELECT rn, first_rk, row_number() over (partition by rn order by case when idx > t.first_idx then num else -1 end desc) as second_rk, num, idx
    FROM first_num
    JOIN (SELECT rn as first_rn, max(case when first_rk = 1 then idx end) as first_idx FROM first_num GROUP BY rn) t
    ON rn = t.first_rn
), filter_num AS (
    SELECT rn, first_rk, second_rk, num
    FROM second_num
    WHERE first_rk = 1
    OR second_rk = 1
)
SELECT sum(num)
FROM (
    SELECT rn, concat(max(case when first_rk = 1 then num end), max(case when second_rk = 1 then num end)) :: integer as num
    FROM filter_num
    GROUP BY rn
) t;

Part Two

987654321111111

挑选出12个数字,不改变顺序,组成最大的数字,比如这里的987654321111

这里不可以再用之前的办法了,但是思路是相似的

  • 先找出第一位数字,最大最靠前
  • 再找出第一位数字后面的最大最靠前
\underbrace{787955955}_{\color{red}\text{当前计算段}} \ 
\underbrace{43232344}_{\color{blue}\text{预留段}}

将长串数字分隔为两段

  • 当前计算段,在这一段中,仅需要找出最大最靠前的数字即可
  • 预留段,必须保留的数字,比如还剩余12个数字要计算,则需要保留11个数字

上面的例子中,计算出了第一个9,则新的两段数字串则变成了

\underbrace{559554}_{\color{red}\text{当前计算段}} \ 
\underbrace{3232344}_{\color{blue}\text{预留段}}
with recursive origin AS (
    SELECT row_number() over () as rn, line, length(line) as len FROM lance_input
), walkthrough AS (
    SELECT 0 as num, 12 as left_nums, rn, line, len
    FROM origin

    UNION ALL

    SELECT num, left_nums, rn, line, len
    FROM (
        with inner_tbl as (
            select * From walkthrough where left_nums > 0
        ), splits as (
            SELECT left_nums, rn, len, x.pos :: integer as num, idx :: integer as idx
            FROM inner_tbl, regexp_split_to_table(line, '') with ordinality as x(pos, idx)
        ), max_valid as (
            SELECT rn, num, idx
            FROM (
                SELECT rn, num, idx, row_number() over (partition by rn order by num desc, idx) as num_rk
                FROM (
                    SELECT rn, num, idx
                    FROM splits
                    WHERE idx <= len - left_nums + 1
                ) t
            ) t
            WHERE num_rk = 1
        )
        SELECT b.num, left_nums - 1 as left_nums, a.rn, substring(line, b.idx + 1) as line, a.len - b.idx as len
        FROM inner_tbl a
        JOIN max_valid b
        ON a.rn = b.rn
        WHERE a.left_nums > 0
    ) t
)
select sum(num) from (
    select rn,string_agg(num::text, '' order by left_nums desc) :: bigint as num 
    from walkthrough 
    where left_nums < 12 
    group by rn
) t;

将12换成2,同样可以计算出第一部分的结果。

发表评论