原题地址:https://adventofcode.com/2021/day/8
第 8 天:七笔划数码管搜索
你刚刚到达洞穴的安全地带,鲸鱼就撞上了洞口,导致洞口坍塌。传感器显示这个洞穴在更深的地方有另一个出口,所以你除了继续前进别无选择。
当你的潜水艇缓慢地穿过洞穴系统时,你注意到潜艇上的四位数七笔划数码管显示器出现了故障;它们一定是在逃跑过程中损坏了。没有它们你会遇到很多麻烦,所以你最好弄清楚哪里出了问题。
七笔划数码管的每个数字通过点亮或熄灭名为 a 到 g 的七个笔划中的任意几个来显示:
0: 1: 2: 3: 4:
aaaa .... aaaa aaaa ....
b c . c . c . c b c
b c . c . c . c b c
.... .... dddd dddd dddd
e f . f e . . f . f
e f . f e . . f . f
gggg .... gggg gggg ....
5: 6: 7: 8: 9:
aaaa aaaa aaaa aaaa aaaa
b . b . . c b c b c
b . b . . c b c b c
dddd dddd .... dddd dddd
. f e f . f e f . f
. f e f . f e f . f
gggg gggg .... gggg gggg
因此,要显示数字 1,只会点亮笔划 c 和 f;其余笔划熄灭。要显示数字 7,只会点亮笔划 a, c, 和 f。
问题在于控制这些笔划的信号在每个显示器上都被弄混了。潜水艇仍然试图通过在信号线 a 到 g 上产生输出来显示数字,但这些电线被随机连接到了笔划上。更糟糕的是,电线/笔划的连接关系对每个四位数显示器是独立混用的!(不过,同一个显示器内的所有数字使用相同的连接关系。)
所以,你可能知道只有信号线 b 和 g 被点亮了,但这并不意味着笔划 b 和 g 被点亮了:唯一使用两个笔划的数字是 1,所以这必然意味着笔划 c 和 f 应该是点亮的。仅凭这个信息,你仍然无法分辨哪根电线(b/g)连接到哪个笔划(c/f)。为此,你需要收集更多信息。
对于每个显示器,你观察一会儿变化的信号,记下你看到的所有十个唯一的信号模式,然后写下一个四位的输出值(你的谜题输入)。利用这些信号模式,你应该能推断出哪种模式对应哪个数字。
例如,以下可能是你在笔记中看到的单个条目:
acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab |
cdfeb fcadb cdfeb cdbaf
(此条目在此处换行以适合显示;在你的笔记中,它全都在一行上。)
每个条目由十个唯一的信号模式、一个 | 分隔符以及最后的四位输出值组成。在一个条目内,使用相同的电线/笔划连接关系(但你不知道实际的连接关系是什么)。唯一的信号模式对应着潜水艇使用当前电线/笔划连接关系尝试显示数字的十种不同方式。因为 7 是唯一使用三个笔划的数字,所以上面例子中的 dab 意味着要显示数字 7,信号线 d, a, 和 b 是点亮的。因为 4 是唯一使用四个笔划的数字,所以 eafb 意味着要显示数字 4,信号线 e, a, f, 和 b 是点亮的。
利用这些信息,你应该能推断出哪组信号线对应那十个数字中的每一个。然后,你就可以解码那个四位输出值。不幸的是,在上面的例子中,输出值中的所有数字(cdfeb fcadb cdfeb cdbaf)都使用了五个笔划,因此更难推断。
现在,请先关注容易识别的数字。考虑下面这个更大的例子:
be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb |
fdgacbe cefdb cefbgd gcbe
edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec |
fcgedb cgb dgebacf gc
fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef |
cg cg fdcagb cbg
fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega |
efabcd cedba gadfec cb
aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga |
gecf egdcabf bgf bfgea
fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf |
gebdcfa ecba ca fadegcb
dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf |
cefg dcbef fcge gbcadfe
bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd |
ed bcgafe cdgba cbgef
egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg |
gbdfcae bgc cg cgb
gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc |
fgae cfgab fg bagce
因为数字 1, 4, 7, 和 8 各自使用了唯一数量的笔划,所以你应该能够分辨出哪些信号组合对应这些数字。仅统计输出值中的数字(每行 | 之后的部分),在上面的例子中,有 26 个使用了唯一笔划数的数字实例(在上面已高亮显示)。
在输出值中,数字 1、4、7 或 8 出现了多少次?
我的代码如下:
with t as(select 'be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb |
fdgacbe cefdb cefbgd gcbe
edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec |
fcgedb cgb dgebacf gc
fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef |
cg cg fdcagb cbg
fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega |
efabcd cedba gadfec cb
aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga |
gecf egdcabf bgf bfgea
fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf |
gebdcfa ecba ca fadegcb
dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf |
cefg dcbef fcge gbcadfe
bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd |
ed bcgafe cdgba cbgef
egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg |
gbdfcae bgc cg cgb
gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc |
fgae cfgab fg bagce' t),
--b as(select unnest(string_split(replace(t,' | ','|'),chr(10)))b from t),
b as(select unnest(string_split(replace(t,' |'||chr(10),'|'),chr(10)))b from t),
line as(select row_number()over() rn,l,l2 from(select string_split(substr(b, 1, instr(b, '|')-1), ' ')l, string_split(substr(b, instr(b, '|')+1), ' ')l2 from b)),
pat as(select rn, array_to_string(array_sort(string_split(unnest(l), '')),'')pat from line),
outp as(select rn, array_to_string(array_sort(string_split(unnest(l2), '')),'')outp from line)
--select count(*)from outp where exists (select 1 from pat where pat.rn=outp.rn and pat=outp)
select * from outp where length(outp)in(2, 3, 4, 7) and outp in (select pat from pat where pat.rn=outp.rn)
;
思路是:
将每行分成模式和输出两部分,这在示例中是|加换行符,在正式输入中没有换行符,所以上述代码提供了正式和示例两种的语句,注释掉一行就能适用其中一种。
将模式和输出都执行空格分隔的字符串转列表,再对列表中的字符串也逐个字符转列表,然后对字符列表排序,再转回字符串。这是因为,有时候模式和输出的字母顺序不同,无法直接比较,排序后就完全一致了。
然后在输出中找到同样行号的模式中长度为2, 3, 4, 7的字符串,对应1、7、4、8四个数字的笔划数。如果在模式中有,那就计数,其实输出中所有的字符串都能在模式中找到,而且不存在无效的字符串,比如aaaa这种,所以只要清点长度是2、3、4、7的字符串就可得到正确答案,不过严谨起见,就按现在这么写。

338

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



