P1738「洛谷的文件夹」大概是最短解
它说,有一堆以 / 分隔的路径,每走进一条路径,就得输出目前为止你得新建多少个文件夹。你可以维护一棵 trie,也可以用字符串哈希表模拟,甚至可以开一坨 set,反正对于看到这篇文章的你怎么写拿什么写都能过。
但我们今天不讲怎么解这题。我们讲怎么把它写短。
因为 perl 声名远扬(在写这种奇妙代码上可以写得很短)于是就用了 perl。
$_=<>;chomp;@d=<>;%s=();$n=0;for(@d){s/\r//g;chomp;$l="";for(split/\//){$l.=$_."/";$s{$l}||=++$n;}print$n-1,"\n"}
读第一行,读接下来的每行,去掉回车,逐段分解路径,拼出所有的前缀,每个前缀只建一次,最后减一(根目录),表示「总共新建了几个」。
然后发现我们似乎不需要 chomp。换行的问题通过字符替换解决了。
<>;@d=<>;%s=();$n=0;for(@d){s/\r\n//g;$l="";for(split/\//){$l.=$_."/";$s{$l}||=++$n;}print$n-1,"\n"}
\r\n 可以用 \s。
<>;@d=<>;%s=();$n=0;for(@d){s/\s//g;$l="";for(split/\//){$l.=$_."/";$s{$l}||=++$n;}print$n-1,"\n"}
想了一会儿,发现我们根本不需要读第一行那个 N,因为所有路径都在后面,且只有路径,没有干扰数据。而且我们可以边读边处理。
<>;%s=();$n=0;for(<>){s/\s//g;$l="";for(split/\//){$s{$l.=$_."/"}||=++$n;}print$n-1,"\n"}
split 那里可以不用正则表达式,可以减少一个字节!
<>;%s=();$n=0;for(<>){s/\s//g;$l="";for(split'/'){$s{$l.=$_."/"}||=++$n;}print$n-1,"\n"}
$l 那里的拼接可以用任意字符。查了一下发现 $" 就是一个空格,所以:
<>;%s=();$n=0;for(<>){s/\s//g;$l="";for(split'/'){$s{$l.=$_.$"}||=++$n;}print$n-1,"\n"}
通过修改变量名到内置的变量名可以做到无需声明即可使用。
<>;for(<>){s/\s//g;$l="";for(split'/'){$s{$l.=$_.$"}||=++$n;}print$n-1,"\n"}
这时突然灵光一闪。路径都是以 / 开头的,直接 split '/' 会产生第一个空字符串。这其实可以接受,但更激进的想法是:我们可以把斜杠替换成空格,再 split,这样就不会有开头那段空元素了,也可以清洗掉换行符。
于是出现了:
<>;for(<>){s/\// /g;$a="";for(split){$s{$a.=$_.$"}||=++$n;}print$n,"\n"}
忘了在哪里看到 for split 这个用法了。\n 可以直接替换成换行符。
<>;for(<>){s/\// /g;$a='';$s{$a.=$_.$"}||=++$n for split;print$n,"
"}
然后 2024 年 11 月的尝试告一段落。接下来是 2025 年高考期间的思考。
因为打印的变量里面没有表达式了,所以可以把 $n 移动到引号里。
<>;for(<>){s/\// /g;$a='';$s{$a.=$_.$"}||=++$n for split;print"$n
"}
后面了解到也能用 map 实现循环。正则表达式分隔符这里用了冒号,可以避免转义。
<>;map{s:/: :g;$a='';$s{$a.=$_.$"}||=++$n for split;print"$n
"}<>
到这里,代码彻底脱离了「解释」的范畴。它不再易读,也不再强调意图。不推荐,也不高效,但它非常干净,而且十分 perl(x
https://www.luogu.com.cn/record/219851781
P.S. Google Keep 用来排版含代码的 Markdown 文章真是灾难。
写于高考期间前往某高级中学有限公司的时候。
2025年8月30日 @ 12:46
可以的👍