LoginSignup
17
7

More than 3 years have passed since last update.

AtCoder Beginner ContestのB問題の最短コードを読む(001-020)

Last updated at Posted at 2018-08-31

はじめに

AtCoderで使用できる言語のバージョンアップ・追加が2020/06/18の夕方に行われました。(実際には、2020/05/13の夜から2020/05/14の昼にかけて一度アップデートされたあと、問題が発生したために巻き戻されていました。)
このアップデートによって、この記事で紹介しているコードの大半が更新されたため、以下の内容は現在のAtCoderにおけるコードゴルフ環境に比べて古いものになります。(定期的な記事の更新は諦めました。)

言語別索引

各問題の最短コードであるものにはByte数が書かれています。

Bash

ABC002 11Byte
ABC004 7Byte
ABC005 18Byte
ABC009 22Byte
ABC010 27Byte
ABC012 17Byte

ABC019

Perl

ABC003 58Byte
ABC015 36Byte
ABC018 54Byte
ABC019 33Byte

ABC006
ABC008
ABC014

Python2

ABC001 55Byte

Ruby

ABC003
ABC018

Sed

ABC007 11Byte
ABC011 11Byte
ABC017 24Byte

Awk

ABC006 42Byte
ABC008 28Byte
ABC014 40Byte
ABC016 39Byte
ABC020 11Byte

ABC002
ABC015

Octave

ABC015

Perl6

ABC013 25Byte

ABC014
ABC015
ABC018

問題

ABC001 視程の通報

$m$

$m \le 70000$のとき${\rm min}(\lfloor m/100 \rfloor,m/1000+50,m/5000+74)$、$m \gt 70000$のとき$89$を2桁に0埋めして出力します。この値が整数でないような入力は与えられません。

Python2(55Byte)
i=input()/1e3;print"%02d"%min(i*10,i+50,i/5-2e-5+75,89)

$70000 \lt m \lt 75000 \Rightarrow m/5000+74 \lt 89$なので、${\rm min}(\lfloor m/100 \rfloor,m/1000+50,m/5000+74,89)$を出力しようとするとそのケースだけWAになってしまいます。このコードでは、$70000/5000+75-2 \times 10^{-5} \lt 89 \lt 70001/5000+75-2 \times 10^{-5}$ということを利用して、$ m = 70001$であってもギリギリで$89$が出力されるようにしています。小数点以下を切り捨てると$70000/5000+75-2 \times 10^{-5}$は$88$となり、これも正しい出力が得られます。

こうすると4数の最小値を出力すればよくなります。
i=input()/1e3で変数iに$m/1000$を代入し、min(i*10,i+50,i/5-2e-5+75,89)"%02d"というフォーマットで出力します。これで小数点以下が切り捨てられます。

ABC002 罠

$W$

文字列$W$中からaiueoの5文字をすべて削除して出力します。

Bash(11Byte)
tr -d aiueo

trコマンドに-dオプションを与えることで、置換ではなく削除を行っています。

Awk(19Byte)
gsub(/[aiueo]/,a)a

gsubは第3引数、今回のようになければ$0を対象にして、第1引数のパターンにマッチする部分文字列すべてを第2引数に置換し、その置換の回数を返します。
置き換える文字列には空文字列を指定し、gsubの返り値を文字列化することで置換の回数が0であっても処理後の$0が出力されるようにしています。

ABC003 AtCoderトランプ

$S$
$T$

文字列中の@atcoderのいずれかの文字に置換することで$S=T$とできればYou can win、できなければYou will loseと出力します。

Perl(58Byte)
print"You ",y,\0!#-%/24,,c?"will lose
":"can win
"for<>^<>

構造はprint(A) for(B)です。

forが後ろにあるのは後置forと言って、for文で実行するのが1つの式のみである場合に使えるものです。今回はprint(A)がその実行する式にあたり、for文では(B)をリストとして解釈して、その値を順番に$_に代入してprint(A)を実行します。

print(A)では、まず共通する"You "がくくりだされていて、残りの部分の出力を三項演算子y,\0!#-%/24,,c ? "..." : "..."で分けています。
この判定の部分にはy,\0!#-%/24,,cというものが使われています。これは置換を行うもので、(文字列)=~y...という形で使われていない場合、自動的に(文字列)の部分に$_を補うので、$_=~y,\0!#-%/24,,c、つまり$_の値を置換しています。

(B)すなわち<>^<>では入力される2つの文字列のXORを取ります。文字列のXORとはどういう値を表すかというと、同じ位置にある文字の文字コードをXORし、そうして得られた数列のそれぞれの値を文字コードとして見ることでまた文字に戻した列です。
これは単一の文字列であり、そういったリストではない値が(B)に来た場合には、1要素のリストであるかのように扱われます。よって、$_<>^<>が代入されてprint(A)が1回だけ実行されます。
結局print "You ",( (<>^<>)=~/[^\0!#-%\/24]/ ? "..." : "..." )と書いたのと等しいです。

y,\0!#-%/24,,cについて説明しますが、その前に、このコードより1Byte長いコードで使われていた/[^\0!#-%\/24]/を用いて、何をしたいかについて説明します。これも$_=~/[^\0!#-%\/24]/という形で評価され、正規表現でパターンにマッチするか否かを返します。

わざわざ文字列のXORを取ったわけですが、これは今回どのような性質を持つかというと、まず同じ文字同士のXORは0になりますから、ある箇所が"\0"であればその位置の文字は$S$、$T$で等しいです。
次に、「一方が@、もう一方がatcoderのいずれかの文字」であるとき、@のアスキーコードは64ですから、文字のXORは以下のようになります。

a c d e o r t
アスキーコード 97 99 100 101 111 114 116
XOR 33 35 36 37 47 50 52
文字 ! # $ % / 2 4

以上2通り以外の文字の組み合わせが存在する場合$S=T$は達成できないので、結局文字列をXORして得られた中に\0 ! # $ % / 2 4以外の文字が存在しないかどうかを確かめることになります。
それが/[^\0!#-%\/24]/というパターンです。

[]で文字集合を表しますが、先頭に^があると、補集合を指定したのと同じ扱いになります。つまり、[^abc]は「abcのいずれでもない文字」を表します。

上で列挙した文字をそのまま書くなら、(/を指定するにはエスケープが必要であることを踏まえて)/[^\0!#$%\/24]/になりますが、これは$%が変数として解釈されてしまい上手くいきません。$もエスケープすれば良いのですが、実は今回はそれを非常に鮮やかに回避する方法があります。それが-、「範囲」です。

#-%とすると「#から%までの文字」を表すことができ、#%のアスキーコードでの間には$しか存在しないので、#-%#$%の3文字を指定したのとまったく等しくなります。これはエスケープを使って#\$%と書くよりも1B短いです。

以上より、(<>^<>)/[^\0!#-%\/24]/にマッチするのは$S=T$となりえないことと同値であることがわかりました。
ではy,\0!#-%/24,,cは何だったのでしょうか。

置換の演算子yは通常はy/A/B/として使います。Aに指定された文字を、対応する位置のBの文字に置き換える役割を持ちます。
ここで、このスラッシュ記号は他の記号に変更することができます。今回は、,をスラッシュの代わりに使っています。こうすることで、/[^\0!#-%\/24]/ではエスケープが必要だったスラッシュ記号がそのまま書けます。この1Byteが短縮ポイントです。

文字集合の補集合を指定するには、y///cという風にcフラグを付けます。
y/A/B/として使う、と言いましたが、このBの部分を今回のように省略すると、Aとまったく同じ文字に置換しようとします。これでは$_に変更が加わりませんが、yを使って行いたいのは別のことです。
実は、yは置換が行われた文字数を返すのです。これさえわかれば$_が変更されようがされなかろうが関係ありません。

yで置換するように指定されたのは、上に列挙した8つの文字「でない」文字すべてです。これで置換が行われた文字数が0より大である、つまりytrueに評価されれば、$S=T$となりえないことを示す文字が存在したということで、You will loseを出力します。1回も置換が行われなければYou can winと出力します。

(<>^<>)=~y,\0!#-%/24,,c ? "will lose" : "can win"で望む出力を選択してprintします。

わざわざ後置forを使ったのは、マッチングを行うのには(<>^<>)=~y...とするのと$_=<>^<>;y...のように変数$_に代入してマッチング対象を暗黙的に指定するのが同じ長さであり、$_に値を代入するときに後置forを使うことでそれらよりも1B短くなるからです。

Ruby(99Byte)
a,b=*$<;puts (0..9).all?{|i|a[i]==b[i]||"@a@t@c@o@d@e@r@"[a[i]+b[i]]}?"You can win":"You will lose"

$<は標準入力で、*splat展開)をすることで入力をすべて読んで改行で区切ったものに展開されます。a,b=*$<によってaに1行目、bに2行目が代入されます。

(0..9).all?は続くブロックの戻り値が0,1,...,9(文字列長は10以下だと保証されています)のすべてにわたって真である場合に真となります。iにそれぞれの数字が代入されてブロックが評価されます。

まずa[i]==b[i]、これは$S$と$T$の同じ位置に同じ文字がある場合を表すほかに、文字列長を超えたインデックスで文字を取得しようとしたときにRubyではnilが返ってくるので、このときもtrueとなることで後ろの評価を行わないようにしています。

次に、"@a@t@c@o@d@e@r@"[a[i]+b[i]]では、文字列のインデックスがa[i]+b[i]という「2文字の文字列」になっています。このような指定をした場合、等しい部分文字列があればそれを、なければnilを返しますが、これは真偽値で言うとそれぞれtruefalseです。つまり"@a@t@c@o@d@e@r@"[a[i]+b[i]]は「a[i]+b[i]"@a@t@c@o@d@e@r@"の部分文字列として存在するか?」という意味です。

$S=T$となれるような互いに異なるa[i]b[i]の組み合わせは、一方が@でもう一方がatcoderのいずれかの文字ですが、このような組み合わせはすべて"@a@t@c@o@d@e@r@"の部分文字列として存在します。逆に他の組み合わせは部分文字列として存在しません。よって$S=T$となれるかが正しく判定できます。

以上2通りでtrueとなるような条件式がa[i]==b[i]||"@a@t@c@o@d@e@r@"[a[i]+b[i]]であり、文字列中のすべての箇所でこれを満たせばYou can winを、そうでなければYou will loseを出力します。

ABC004 回転

$c_{0,0}\quad c_{0,1}\quad c_{0,2}\quad c_{0,3}$
$c_{1,0}\quad c_{1,1}\quad c_{1,2}\quad c_{1,3}$
$c_{2,0}\quad c_{2,1}\quad c_{2,2}\quad c_{2,3}$
$c_{3,0}\quad c_{3,1}\quad c_{3,2}\quad c_{3,3}$

入力を180度回転させて出力します。

Bash(7Byte)
rev|tac

それぞれrevは左右を、tacは上下を反転するコマンドです。

ABC005 おいしいたこ焼きの食べ方

$N$
$T_1$
$T_2$
$:$
$T_N$

$T_{1..N}$の最小値を出力します。

Bash(18Byte)
read;sort -n|sed q

1行目も一緒にsortしてしまうと答えが変わってしまうので、readで読み飛ばしています。sort -nで残りの行を数値としてsortした後、sedに渡しています。

このsed qというのはsedコマンドにスクリプトqを与えたもので、qというのは実行されるとその時点でパターンスペースの内容を出力してsedを終了する命令です。つまり、まずsedは1行目を読み込んでスクリプトを実行するのですが、今持っている行の内容を出力して終了せよという命令が一番最初に実行され、その通りに1行目だけ出力して残りの行の処理を行わないのです。これで入力の1行目だけが抜き出せて、sort済みであるため求める答えとなります。

入力の1行目だけ抜き出すというのを実現するために、例えばsedで1行目以外を削除したり、コマンドheadを使ったりする方法がありますが、最も短いのがsed qです。

ABC006 トリボナッチ数列

$n$

初項、第2項が$0$で第3項が$1$であるトリボナッチ数列の第$n$項を$10007$で割った余りを出力します。

Awk(42Byte)
{for(r=1;--$1;r=p+(p=q)+(q=r%10007))}$0=0p

計算部分を読みます。順にpqrを持って、(p,q,r)->(q,r,p+q+r)と更新していきます。初期値は(p,q,r)=(0,0,1)ですが、未定義の変数は数値としては0なのでr=1だけ実行しています。

r=p+(p=q)+(q=r)とすることで、左から順番に評価されるため更新がうまくいきます。qに代入するときだけ余りを取るとrの値が$10007$を超える可能性がありますが、出力には関係なく、誤差が生じない大きさであるため問題ありません。

このループでは1回ごとに数列が1項ずつ進んでいくので、ループ回数は$n-1$回であるべきです。前置デクリメント--$1を使い、評価に先んじて1減算することで実現されています。後置デクリメントであれば、ループは$n$回回ることになります。

出力の部分ですが、pは0となる場合だけでなく、空文字列である場合もあります。この場合、通常であれば+を前置して数値化し、それをさらに文字列化する必要があるのですが、この問題のジャッジはleading zeroを許すので、0pと書いて先頭に0をくっつけた文字列にしています。これを$0に代入することで出力が行われます。

Perl(49Byte)
($%,$c)=($c%10007,$\+$c+($\=$%))for++$\..<>;print

($%,$c)=($c%10007,$\+$c+($\=$%))をループで実行しています。$\$%$cの順に数列になっていて、これを更新して1項ずつ進めていくのですが、AWKとは式の評価の方法が微妙に違うので、$c=$\+($\=$%)+($%=$c%10007)と書いても変数の更新のタイミングがAWKとは変わって上手くいきません。(これについて、tailsさんと%20さんに理由を教えていただいたので、最後に書いておきます。)
ただ、$\+$c+($\=$%)は望んだとおりに($\の値が取り出されてから$\$%が代入されるように)評価されるので、$%$cについてのみリストの代入(($%,$c)=($c,$\+$%+$c))という形で更新しています。

トリボナッチ数列の第0項として1を追加しても、残りの要素は等しくなります。よって、$\を第0項と見て1を代入してから数列を進めるループを$n$回回すことによって、$\が第$n$項を表すようにできます。(わざわざこうしているのは、ループを$n$回回す方が$n-1$回回すより短く書けるからです。)

$n$回のループは、for文に$1..n$という範囲を使うことで行えます。それがfor(++$\..<>)で、先ほどの$\に1を代入するという操作も前置インクリメントを使って一緒に書いています。

ループが1回だけ回るとき、$\$%の初期値となります。未定義変数は空文字列になってしまうのですが、$%は初期値が0の組み込み変数であるため、$b$xではなく$%であるからこそ、$\は正しい「数値」を持つことができます。

最後に出力をするのですが、print関数は$\という変数に値が存在すればそれを出力の末尾に追加で出力します。これを期待して$\に答えとなるべき数列の項を保存してあるので、print関数を引数なしで呼び出すだけで出力できます。改行が出力できませんが、この問題では出力の末尾に改行が必要ではないのでACできました。

さて、$\+($\=$%)+($%=$c%10007)が思うように動作せず、$\+$c+($\=$%)は動作することの理由についてです。

まず、$\+($\=$%)という式の断片は、演算子+の左右にそれぞれ「左辺値$\」と「左辺値$\に影響を与える式」が来ています。このように、2項演算するときに「左項が左辺値」かつ「右項が『 "左項である左辺値" に影響を与えるような式』」であれば、その"左項である左辺値" は値を取り出される前に影響を受けます。
つまり、$\+($\=$%)は、「$%を代入する」という右項が$\に与える影響が左項にも伝播してしまい、$%+$%と同じ値になってしまうということです。

$\+$c+($\=$%)については、2項演算子+が左結合であることを踏まえて(($\+$c)+($\=$%))と解釈されます。このとき、($\+$c)が先に解釈され、特に影響を与える代入などは存在しないためにそのまま値が得られます。
この得られた値と($\=$%)を足し算するのですが、左項はもはや左辺値ではなく右辺値であるため、これも影響は伝播しません。結局、$\=$%を実行しながらも$\+$c+$%という望み通りの値が得られるということです。

これを利用して、-$x+($x=$_)と書くことで、前のループの値と今のループの値の差を計算しつつ変数$xを更新するということが可能です。
-$x+を評価するタイミングではすでに計算された右辺値であって左辺値ではないため、($x=$_)の影響が伝播しないということです。

Perlで左辺値になるのは、「変数そのもの」「変数への代入」「前置インクリメント/デクリメントされた変数」「+が前置された変数」のみです。

ABC007 辞書式順序

$A$

小文字アルファベットを使った$A$よりも辞書順比較で真に小さい空でない文字列を、存在しなければ-1を出力します。

Sed(11Byte)
/^a$/c-1
ca

/^a$/というパターンは"a"という文字列にマッチします。入力が"a"であるとき、条件を満たす出力すべき文字列は存在しないので-1を出力します。
そうでなければ、"a"という文字列は他のいかなる入力より真に小さいので、これを出力します。

ABC008 投票

$N$
$S_1$
$S_2$
$:$
$S_N$

$S_{1..n}$に最も多い回数出現したものから1つ出力します。

Awk(28Byte)
NR>n+=$0=s=++a[$1]<a[s]?s:$1

配列aによって出現回数をカウントしています(AWKの配列は常に連想配列です)。s=++a[$1]<a[s]?s:$1によって、変数sにその時点で最も多く出現した文字列が保存されます。(出力のため、$0にも代入しています。)

1行目も一緒に処理していますが、制約から1行目と等しい文字列は$S_{1..n}$に存在しないことを利用して、1行目が答えとして出力されないようにしています。というのは、境界条件を調節して、出現回数が同じ文字列については常に1番最後に出現したものが保存されるようにすることで、$S_{1..n}$がすべて異なる場合(出現回数1回が最大である場合)も最初に出現した1行目ではなく$S_N$が出力されるようになっているということです。

最終行を処理した後に出力をしたいので、最終行であるかどうかの判定をします。

変数nに、行ごとのsの値が数値として加算されていきます。sは、1行目の$N$という文字列を処理した時点では$N$であり、その後は常にアルファベットの文字列(=0)ですから、nはプログラムの実行中常に$N$という値を持ちます。

変数NRは1-indexedで今何行目を処理しているかを表す変数です。$N+1$行目を処理し終わった後に出力を行いたいので、NR>nとすることでそのような条件があらわせます。これを満たした時点で$0sが代入されている)が出力されます。

Perl(30Byte)
$_[++$$_]=$_ for<>;print$_[-1]

これはコードゴルフという点において非常に教育的なコードだと思います。

まず$$_ですが、これは${$_}の短縮形です。ここで、$_には何か文字列が入っているとします。(空文字列でも良いですし、数値も文字列ですが、今回のコードでは文字列です。)

${}というのはデリファレンス(dereference)と言って、いろいろ使い方がありますが、文字列という「何かへのリファレンス(reference、参照)」になっていない値をデリファレンスするとどういうことが起こるのでしょうか?
これはシンボリックリファレンスと呼ばれ、単にそれを変数名に持つ変数となります。
例えば、$_=="hoge"であった場合、$$_$hogeと同じ変数を表します。

次に$_[++$$_]=$_です。

これは、配列@_++$$_番目に$_を代入していると読むことができます。配列を表すのは@ですが、それに添え字でアクセスした時点で配列ではなくスカラ(単一の値)になるので、@_[++$$_]ではなく$_[++$$_]です。(@_$_は同じ変数名のように見えますが、配列とスカラは名前空間が違うので問題ありません。)

これを後置for、for<>でループしています。

<>forのようなリストが期待される文脈(「リストコンテキスト」といいます)に置かれると入力をすべて読んで改行区切りにしたリストになりますから、$_に入力が1行ごとに格納されて$_[++$$_]=$_が実行されます。
$_forは有効な変数名なので、パースが行われるように$_ forと空白を開けています。

同じ文字列をデリファレンスしたものは当然同じ変数を表しますから、結局デリファレンスを短く書けるハッシュのように扱って出現回数をカウントし、それに応じて配列@_に保存しているということです。

ループが終了した後、配列@_の$i$番目には、$i$回出現した文字列の中で最も最後に出現したものが代入されています。配列は必要な分だけの長さがぴったり確保されるので、@_の一番後ろ(インデックスが最も大きな場所)が出力すべき文字列を表しますが、これは負の数が配列の後ろから数えることを利用して-1で添え字づけることで取得できます。
<>は読み込んだ文字列の改行を削除しないので、そのままprint$_[-1]とすることで改行付きで出力することができます。

この改行について1つ注意点があります。それは、${"a\n"}${"a"}は異なる変数であるということです。今回、これは良い方に作用しています。
そもそも、記号や数字から始まる変数名$%$262144は組み込みの特殊変数であり、特に数字の変数は自由に書き込みができないように設定されています。
改行がなければ$N$を読み込んだときの$$_は数字名の変数であり、インクリメントできないためREとなるのですが、改行がついているため別の変数であると認識され、REを免れました。

ABC009 心配性な富豪、ファミリーレストランに行く。

$N$
$A_1$
$A_2$
$:$
$A_N$

$A_{1..N}$の重複を除いたうえで、2番目に大きな値を出力します。

Bash(22Byte)
read;sort -nru|sed 2!d

まずreadで1行読み飛ばします。$N$も数値であるため、混ざっているとうまくsortできないからです。
残りの入力を、sortにオプションを与えて並び替えます。

-nはそれぞれの行を数値として解釈して比較させます。
-rは逆順、つまり大きな順に並べ替えさせます。
-uは同じ要素を1回だけ出力させるようにします。

これらのオプションを与えて並び替えると、上から2行目が出力すべき値になります。

sed2!dというスクリプトを実行します。これは「2行目(2)でないとき(!)行の内容を削除(d)する」という風に分解できます。つまり望む行以外を削除するということです。
!を書くのにエスケープが必要でないことも短縮ポイントです。

ABC010 花占い

$n$
$a_1 \quad a_2 \quad ... \quad a_n$

数列のそれぞれの要素を、偶数でも3で割って2余る数でもなくするために減じる必要のある数の合計を出力します。

Bash(27Byte)
read;tr 1-9\  010123010+|bc

$a_{1..n}$は1以上9以下の数なので、それを対応する数に置き換え、和を求めればよいです。

trコマンドで、1-9とエスケープしたを、それぞれ010123010の対応した数と+に置換すると、それを式として評価した値が答えになります。bcコマンドは標準入力から式を読み込む計算機なので、それに計算させます。

実は、式を評価するだけの問題がABC050-Aにあります。また、上のコードは範囲をうまく使って010123010->010-3010とすることで1Byte縮む余地があります。ではなぜ、ABC050-Aではbcが使われていないのでしょうか?また、なぜ上のコードが最短コードとなっているのでしょうか?

実は、現在AtCoderのbashでコマンドbcを呼び出すことはできないのです。どの時点からかはわかりませんが、bcを呼び出そうとするとコマンドが見つからないとしてREになってしまいます。

ABC011 名前の確認

$S$

$S$の先頭1文字を大文字に、他の文字を小文字にして出力します。

Sed(11Byte)
s/.*/\L\u&/

s/pattern/replacement/中のreplacementでは、&patternにマッチングした文字列全体を表すことができます。
\Lは続く文字列をすべて小文字にする効果を、\uは続く1文字だけを大文字にする効果を持ちます。この2つをこの順番で書くことによってのみ、望む変換、つまり先頭以外すべて小文字で先頭1文字だけを大文字にする変換ができます。

ABC012 入浴時間

$N$

$N$(秒)を$hh : mm : ss$の形式に変換して出力します。

Bash(17Byte)
date +%T -ud@`dd`

dateは時刻を表示するコマンドです。表示形式を+%Tで指定していて、これはまさに$hh : mm : ss$という形式です。
-dオプションで表示する時刻を変えることができて、-d@秒数として1970年1月1日午前0時0分0秒からの経過秒数を与えることができます。日付は関係ないので、今回はこれで事足ります。`dd`で入力を読み、dateへの引数に埋め込んでいます。

ところが、これはローカル時間であるため、UTC+9にあるAtCoderのジャッジサーバーでは9時間先の時間が表示されてしまいます。それを防ぐために-uオプションを与えてUTC+0にしています。

ABC013 錠

$a$
$b$

${\rm min}(|a-b|,10-|a-b|)$を出力します。

Perl6(25Byte)
say 5-abs (get- get)%10-5

場合分けをします。

  • $a \ge b$のとき)(get- get)%10は$a-b$となります。
    • $a-b \ge 5$のとき)abs (get- get)%10-5は$a-b-5$となり、出力は$10-a+b = 10-|a-b|$です。$|a-b|\ge 5$より$|a-b|\ge 10-|a-b|$です。
    • $a-b \lt 5$のとき)abs (get- get)%10-5は$5-a+b$となり、出力は$a-b=|a-b|$です。$|a-b|\lt 5$より$|a-b|\lt 10-|a-b|$です。
  • $a \lt b$のとき)(get- get)%10は$a-b+10$となります。
    • $a-b+10 \ge 5$のとき)abs (get- get)%10-5は$a-b+5$となり、出力は$b-a=|a-b|$です。$|a-b|\le 5$より$|a-b| \le 10-|a-b|$です。
    • $a-b+10 \lt 5$のとき)abs (get- get)%10-5は$b-a-5$となり、出力は$10+a-b=10-|a-b|$です。$|a-b|\gt 5$より$|a-b|\gt 10-|a-b|$です。

よって正しい答えを出力できることが確かめられました。

ABC014 価格の合計

$n \quad X$
$a_0 \quad a_1 \quad ... \quad a_{n-1}$

$i \in \{0,1,...,n-1\}$で$X$の$i$bit目が立っている$i$について、$a_i$を足し合わせた値を出力します。

Awk(40Byte)
RS=FS{x%2<1||a+=$1;x=x/2+$2}END{print+a}

FSのデフォルト値は" "です。RS" "を代入することで、入力を空白区切りで読み込むようになります。
このRSへの代入は1行目の処理中に初めて行われるので、そのときすでに読み込まれている$1$=n$、$2$=X$には影響しません。
また、" "という文字列はtrueと解釈されるので、毎回ブロックが実行されます。

入力の1行目を処理しているとき、ブロック内部ではどのような処理が行われるか確かめます。
xはこの時点では未定義なので、x%2<1trueです。したがってa+=$1は実行されません。
x=x/2+$2によってx$2$=X$が代入されます。

以降の処理ではRS" "となっているので、各$a_i$が各行に1個ずつあるような具合で読み込まれ、処理されます。
xには$X$が代入されているのでした。これを毎回2で割っていけば、2で割った余りを見ることで順に各bitを取り出すことができます。
またこのときわざわざ整数に切り捨てなくても、x%2>=1であればbitが立っていると判定できます。
よってx%2<1||a+=$1で正しい条件分岐が行え、bitが立っているときだけ$a_i$をaに加算できます。
RS" "であるため$2は存在せず、x=x/2+$2はただx/=2と同じ意味になります。

ENDブロックで答えを出力します。aへの加算が1回も行われなかった場合は空文字列が出力されてしまうので、+aで数値として評価しています。

Perl(46Byte)
<>=~$";map$x+=$'>>$i++&1&&$_,glob<>;print$x,$/

Perlゴルフで使われる主要な入力の読み込み方2つが両方とも出てきています。

まず入力の1行目ですが、<>=~$"を実行して読み込んでいます。これは<>$"をマッチングさせていると読むことができます。
$"は初期値が(スペース文字)である組み込み変数なので、<>=~$"<>=~/ /と等しく、$n$と$X$の間のスペースにマッチするはずです。
マッチングが行われると、$`に「マッチした位置より前方の文字列」、$&に「マッチした部分文字列」、$'に「マッチした位置より後方の文字列」が代入されます。したがって、<>=~$"を行うと$`が$n$と、$'が$X$とそれぞれ等しくなります。これで変数に明示的に代入することなく入力された値を保存できました。

次にmap関数が実行されます。第1引数は$x+=$'>>$i++&1&&$_で、これは実行される無名関数のようなものです。第2引数はglob<>であり、ここで2行目を読み込んでいます。まずこれの説明をします。

glob関数は文字列を引数にとって、文字列中にワイルドカード文字が存在する場合、カレントディレクトリのファイル名として展開して列挙したリストを返します。しかしPerlゴルフではそういう使い方はされません。
そもそも与えた文字列、つまり<>で読み込んだ入力の2行目にはワイルドカード文字は存在しません。このような場合、globは文字列を空白で区切ったものをリストとして返します。つまり、split関数の代用として用いることができます。(パフォーマンスは悪くなります。)

map関数は第2引数以降のリストの個々の要素を$_に代入しつつ第1引数のブロックや式を実行し、結果をリストにして返します。今回、返ってきたリストは使用されず、mapはただfor文と等しくリストでループを回すために用いられています。必要な空白の関係でmapを使ったほうが短くなるのです。

$x+=$'>>$i++&1&&$_を読みます。

上で述べたことより、$'は$X$でした。これを$i(ループごとにインクリメントされ、ちょうど今見ている要素がリストの何番目であるかを示します)だけ右にbitシフトすると、1bit目を見ることでリストのその位置の要素を足し合わせる必要があるか否かを判定できます。
&1で1bit目だけを取り出します。&&は左の項がtrueであれば右の項を、falseであれば後を評価せずfalseになった左の項をそのまま返しますから、左の$'>>$i++&11である場合$x+=$_$_はリスト$a$の$i番目です)、0であれば$x+=0と同じ意味になり、$xに出力すべき答えが保存されます。

最後に出力をしますが、末尾に改行が必要です。組み込み変数$/は初期値が改行文字なので、これを一緒に出力することで改行を付加することができ、"\n"などと改行を文字列として書くより短くすみます。

Perl6(46Byte)
get~~/\s.+/;say [+] map $/+>.++%2* *,get.words

Perl6でも、マッチングによって入力の読み込みをするテクニックが使われます。ただ、Perlでの$`$'はPerl6においてそれぞれ$/.prematch$/.postmatchというとんでもなく長い名前になってしまうので、ゴルフとしては実用的でありません。

ABC097-Aで用いたようなマッチングの時に設定されたキャプチャーは有用です。しかし、今回は1行目では$X$しか必要ではありません。このとき、もっと短く書く方法があります。
それがPerlでいう$&です。これはPerl6においては$/であらわされるので、長さは変わっておらず2文字そのままです。

$X$にマッチングさせるには、「空白1文字と任意の文字複数」というパターンが使えます。これは/\s.+/となります。(+は「1文字以上」です。)Perl6のパターンでは、空白は読みやすくするためのものであるとして自動で削除してしまうので、/ .+/と書いてもうまくいきません。スペースやタブなどの空白文字を表す\sを使う必要があります。

$0$1$/を使って入力を読み込む方法3つを並べてみます。
get~~m:g/\d+/
$/=get.words
get~~/\s.+/
このように、入力の必要な部分に応じて読み込む最短の方法が変化します。

とにかく、$/で$X$を表すことに成功しました。次のsay [+] map $/+>.++%2* *,get.wordsを読みます。

ここでもまたmapを使っています。構造を説明すると、まずget.wordsで2行目を空白区切りにしたリストを得て、それをmapで変換し、[+]を使ってfoldし、出力しています。
[+]というのは[]+の組み合わせであり、[]は中に置かれた演算子でリストをfoldすることができるメタ演算子です。

mapでリストを変換するのには無名関数を使っています。それが$/+>.++%2* *です。

Perl6で無名関数を作るのには、ブロックを使うほかに、*を使う方法があります。これは、*に引数が1つづつ適用される関数になるというもので、例えば*+5と書くことで、引数を1つとって5と足し合わせたものを返す関数を作れます。

実際、say (*+5).(4)というコードは4を引数として関数を呼び出し、4+5、つまり9を出力します。

$/+>.++%2* *には*が2つ存在しますが、1つ目は乗算の演算子であり、$/+>.++%2*(引数)という関数であると解釈されます。

$/+>.++%2を読みます。$X$のbitに応じてこれが1 or 0の値をとれば、それとリストの要素が掛け算されることで$0$ or $a_i$となり、これでリストを変換することで、足し合わせて答えを得られます。実際にそうなっています。

まず、+>は右にbitシフトする演算子です。.++というのは、++(後置インクリメント)をメソッド呼び出しの形で書いていて、呼び出し元の値が存在しない場合は自動的に$_が補われるので、$_++と等しいです。$_はソースコード中のほかの場所では使われていないので初期値の(Any)であり、数値としては0です。上で紹介したPerlのコードにおける$iと同じ役割を果たしています。

+>%の演算子の優先順位は等しいので、$/+>.++%2($/+>$_++)%2と解釈され、bitシフトして1bit目を取り出していることになります。
結局$X$のbitに応じて1 or 0の値を取るという目的に合致していますから、先に述べたようにリストを$0$ or $a_i$に変換することができました。足し合わせて答えを得て出力します。

ABC015 高橋くんの集計

$N$
$A_1 \quad A_2 \quad ... \quad A_N$

$A_{1..N}$のうち$0$でない数だけを抜き出し、その平均値を切り上げて出力します。

Perl(36Byte)
print!<>|.9+$a/grep{$a+=$_;$_}glob<>

まず!<>が実行され、1行読まれます。これは後で必要になります。
次にglob<>で入力の2行目を空白区切りにしたリストを得て、それを引数に関数grepを実行します。

関数grepは、第1引数で受け取った式やブロックに第2引数以降を次々と適用し、trueを返すもののみからなるリストを作ります。
今回実行するブロックは{$a+=$_;$_}です。引数が自動的に$_に代入されます。
このブロックでは、$a$_を足し合わせ、$_を返しています。$_!=0のときtrueになるので、結局grepは$A_{1..N}$のうち$0$でない数だけを抜き出したリストになります。また、$aはそのリストの総和になります。($0$である要素は足しても和に影響しません。)

リストはスカラコンテキストで評価するとその長さになるので、$a/grep...で平均値を求めることができます。ABC006-Bで出てきた左辺値の性質によって、$aは左から順に見ていくとgrepより先に出現しますが、計算時にはgrepを評価した後の値(リストの総和)になります。

あとは$a/grep...を切り上げるのですが、テストケースが微妙に弱いため0.9を足して切り下げることでACできます。bit演算を行うときは数値が整数型に切り下げられるという事実を利用して、.9+$a/grep...0bitwise ORを計算すればよいです。ここで0の代わりに!<>を使っています。|演算子は右項と左項のどちらかが数値であればもう片方も数値として評価するので、!<>は数値の0になるからです。

今回のジャッジでは出力の末尾に改行は必要ありません。

Octave(42Byte)
disp(ceil(mean((x=dlmread(0)(2,:))(!!x))))

括弧がネストしていてわかりづらいです。適当に空白を入れて構造を明らかにしてから、中から外に向かって読んでいきます。
disp( ceil( mean( ( x=dlmread(0)(2,:) )(!!x) ) ) )(入れてもわかりづらかった……)

まず、x=dlmread(0)(2,:)です。dlmreadは入力全体を行列として読み込む関数で、第1引数にファイルハンドルを指定します。0はstdinを表します。
入力を読み込んだ結果、今回は1行1列目が$N$、2行目が数列$A$になります。これの数列$A$の部分だけを取り出すときには、添え字を2行目を表す2と列全体を表す:(これは1番目から最後まで、すなわち1:endの省略形です)にします。Octaveは1-indexedです。
こうして取り出した数列$A$を$N$要素の行ベクトルとして変数xに代入します。(Octaveは1-indexedです。)

次に( x=dlmread(0)(2,:) )(!!x)xの値が分かったので括弧を外してx(!!x)です。
これは条件に沿ってベクトルを取り出す書き方です。例えば、aの3より大きな要素だけを取り出したい場合は、a(a>3)と書くことができます。
ではx(!!x)はどのような条件を表すのか?まず、!は0をtrueに、それ以外をfalseにします。2回適用することで、0以外をtrueに、0をfalseにします。制約よりxに負の値は存在しないため、結局これはx(x>0)と書いたのと変わりありません。

こうやって取り出したすべての要素が0より大である数列の、平均を求め、切り上げ、出力します。
順にmeanceildispという関数があるので、適用します。

今回のジャッジは末尾に改行がなくても通ったりと柔軟なタイプなので、関数dispによって出力の先頭に空白文字が存在してもACできました。

Perl6(44Byte)
get;$_=@(get.words);say .sum/.grep(+*)+.9+|0

1行読み飛ばします。$_get.wordsを代入することで、これに対するメソッド呼び出しが省略して書けることを利用します。

@()で囲っている理由は、wordsの返り値をリスト型に強制するためです。
wordsSeqという型を返します。これは何かというと、遅延評価のリストであり、実は1回評価して値を取り出すとデータが消費されてしまい、もう二度と使えないのです。(2度目に使うとREになります。)
コードの後ろのほうで.sum.grepによって2回使う予定なのでこれでは困るため、リストにしてからデータを保存しています。
ABC066-Aで行ったように、+<<などを適用してもリストのデータが得られます。(あちらは、別の理由から+<<をつけたので、この問題が表面化しませんでした。)

そうして得られたリストを$_に持ってsay .sum/.grep(+*)+.9+|0を実行します。

まず、.sum$_.sumの省略形、.grepもそう)は総和を求めます。次に、.grep(+*)というのは、リストのうち関数(+*)に値を渡したときの戻り値がtrueであるものだけ抜き出す関数です。(+*)では値が数値化され、0でない数であればtrueに、0ならばfalseに評価されます。
よって.grep(+*)でリスト$_のうち0でない数だけが抜き出されたリストが得られます。実はリストは、数値として評価するとその長さになるため、.sum/.grep(+*)で「数列の総和」を「数列中の0でない数の個数」で割った値が得られます。

0.9を足し、bitwise ORで切り捨てをするのは上のPerlコードと同じです。+|bitwise ORの演算子です。

Awk(44Byte)
t{for(;i++<NF;)c+=s<s+=$i}t++,$0=int(s/c+.9)

1行目の情報は、2行目ではNFという「現在の行のフィールド数」を表す変数によって得られるので、必要ありません。読み飛ばします。
読み飛ばし方についてですが、カウント変数tを用意します。上のコードでは2度出現しており、2度目の出現で(後置で)インクリメントされています。
これによって、1行目の時点ではブロックは実行されず$0の出力も行われない、2行目ではブロックも実行されるし出力もされる、という風にできます。

ブロックの内部では、変数iを1からNFまでインクリメントしてループを回しています。(後置デクリメントなので、i<NFという等号がついていない比較ですが、しっかりNFまでループが回ります。)
s<s+=$is<(s+=$i)と評価され、両辺からsを引くと0<$iになるので、s$iを足すのとc0<$iを足すのを同時に行っています。これでsは数列の総和に、cは数列の$0$より大きな要素の個数になります。

最後に、$0int(s/c+.9)を代入します。これも0.9を足して切り下げるとACできるという嘘解法に基づいています。
これはt++を左の式にした範囲パターン内なので、2行目を処理しているときだけ代入と出力が行われます。

ABC016 A±B Problem

$A \quad B \quad C$

$A+B=C \land A-B=C$ならば?、$A+B=C \land A-B \ne C$ならば+、$A+B \ne C \land A-B=C$ならば-、$A+B\ne C \land A-B \ne C$ならば!を出力します。

Awk(39Byte)
$0=$1+$2-$3?$1-$2-$3?"!":"-":$2?"+":"?"

$A+B-C \ne 0$であれば、$A-B-C$が0であるかそうでないかで-!を分けることができます。
そうでなかったとき、$A+B=C \land A-B=C \Leftrightarrow A+B-C=0 \land B=0$なので、$B$が0であるかそうでないかで?+を分けることができます。

ABC017 choku語

$X$

文字列$X$がchokuだけに分解できるならYES、できないならNOを出力します。

Sed(24Byte)
s/ch//g
/[^oku]/cNO
cYES

まず、s/ch//gchという部分文字列をgフラグによってすべて空文字列に置換します。
こうして残った文字列にokuの3文字以外が存在すればそれはchoku語ではないため、NOを出力します。
この判定は、文字集合[oku]の補集合である[^oku]にマッチングするかどうかで行えます。

ABC018 文字列の反転

$S$
$N$
$l_1 \quad r_1$
$l_2 \quad r_2$
$:$
$l_N \quad r_N$

文字列$S$の(1-indexedで)$l$文字目から$r$文字目までを反転する操作を$N$回行った後の文字列を出力します。

Perl(54Byte)
$\=<>;map$_=reverse,substr$\,$`-/ /,$'-$`+1for<>;print

$\=<>で$S$を読み込んでいます。変数$\を変更していけば、最後にprint関数を呼び出すだけで$\は自動的に出力されます。<>は改行付きで読み込むので、その改行が変更されず残っていれば出力末尾の改行については問題ありません。

後置forで残りの入力をすべて読み込み、ループを実行します。
ループの中ではmap関数を実行しています。第2引数以降が1つ1つ$_に代入されて$_=reverseが実行されます。今回はsubstr$\,$`-/ /,$'-$`+1ただ1つです。

入力の3行目以降を処理しているならば、$`-/ /は$l-1$と等しく、$'-$`+1は$r-l+1$と等しいです。(これについては少し下のPerl 59Byteの元最短コードのほうに詳しく書いてあります。)
substr$\,$`-/ /,$'-$`+1は文字列$\の$l-1$文字目(0-indexed)から$r-l+1$文字の部分文字列を表します。これが反転するべき部分文字列です。

入力の2行目を処理しているならば、/ /のマッチングが失敗するため$`,$'はどちらも未定義値のままです。/ /0となるので$`-/ /は$0$と等しく、また$'-$`+1は$1$と等しいため、substr$\,$`-/ /,$'-$`+1$\の最初の1文字を表します。これを反転しても影響ありません。

各ループでsubstrで得られた部分文字列を反転すれば、求める答えが得られることがわかりました。$_=reverseを実行することで、それが実現できます。
関数mapのブロックや式で使う$_は元の値のコピーではなくエイリアスなので、$_の値を変更すると元の値も変更されます。(代入不可能な値、つまり左辺値でない値だとエラーが出ます。)
関数substrには代入することが可能なので、mapによって実行される式の内部で$_を書き換えることで元の文字列を変更できます。substr
よって$_=reverse$_に自身を反転した文字列(関数reverseは暗黙で$_を反転します)を代入することで、その部分文字列を反転することができます。reverse

最後にprintで出力します。制約から、$\末尾の改行を変更してしまうような範囲は指定されないので、これで末尾に改行がついたまま出力されます。

Perl(59Byte)Perlの元最短コード
@S=<>=~/./sg;@S[@@]=@S[reverse@@=$`-/ /..$'-1]for<>;print@S

まず、<>=~/./sgによって文字列を文字のリストに分解します。gフラグはマッチするものすべてにマッチさせます。sフラグは、ここでは.を改行にマッチできるようにする役割を持っています。<>=~/./sgはリストに代入しようとすると、ただマッチングが成功したか否かを返すのではなく、マッチングする部分文字列すべてをリストにして返します。

こうして文字のリスト@Sが得られました。リストの添え字として範囲などのリストを使うと、リストのスライスというものが作られ、これは変更可能なので、スライスを反転したものをスライスに代入することで部分文字列の反転ができます。

2行目以降をfor<>で一気に読み込み、1行ずつ$_に代入して@S[@@]=@S[reverse@@=$`-/ /..$'-1]を実行します。

まず、$`-/ /..$'-1が評価されます。$_/ /のマッチングが試みられ、2行目では失敗し、3行目以降では成功します。まず3行目以降のことを考えます。
マッチングに成功すると、$`にマッチした位置より前方の、$'に後方の文字列が代入されますから、それぞれ$l$と$r$になるはずです。さらに、/ /は成功したので1(つまりtrue)となります。よって、$`-/ /..$'-1l..rという範囲の両端から1引いて0-indexedにしたものだとわかります。
(ここで、最初の$`を評価した時点では/ /が実行されていないから、値が入っていないのではないかと思う人もいるでしょう。しかし望んだとおりに動作します。理由は、最初に$`を見るときは「$`という変数を使う」ということが確定するのであって、実際に値が取り出されるのは式のかたまりを見終わってからだからです。今回は、取り出される前に/ /も見られ、こちらはその見られたタイミングでマッチングが行われるため、実際に$`の値が取り出されるタイミングではちゃんと値が入っているようです。これはABC006-Bに出てきた左辺値の効果です。)

ともかく、$`-/ /..$'-1で望む範囲を得られました。これは2回使うので@@というリスト(これはリストの名前が@であるだけの普通のリストです)に代入しておきます。

@Sにリスト@@でアクセスします。得られる値はスカラではなくリストのスライスなので、$S[@@]ではなく@S[@@]です。@S[@@]=reverse@S[@@]とすると、望む範囲の@Sのスライスが反転されて同じ範囲に代入されています。reverse@S[@@]というのは、@S[reverse@@]と等しく、上のコードではこちらを使っています。

少し戻って、「入力の2行目で/ /に失敗したらどうなるか」を考えておきます。このとき、$`にも$'にも値は入っていませんから、$`-/ /..$'-1というのは0-0..0-1、つまり0..-1になります。これは範囲の始点が終点より大きいため有効な範囲ではありません。@@には空リストが代入され、@Sは何ら変更されません。

最後に、@Sを出力します。print関数に引数として与えると、リストは展開され、要素を1つ1つ引数に与えたのと同じ扱いになります。print関数はデフォルトでは引数と引数の間に余計なものは出力しないので、これで文字のリストが続けて出力されることになります。

末尾に改行をつける必要がありますが、1番最初に<>=~/./sgで文字列を分解した際、改行文字も.にマッチングさせたので、実は@Sの末尾に改行文字が入っていました。これが出力されるのでACできます。

Perl6(60Byte)
$_=get;($/=get.words;.substr-rw($0-1..^$1).=flip)xx get;.say

まず$_に$S$を読み込みます。

次に(~~~)xx getということをしています。xxはリスト繰り返しの演算子であり、これは(~~~)というリストをget回(2行目なので$N$です)繰り返すという意味で、このときリストの内容は繰り返すたびに評価されます。
今回は作られるリストには興味がなく、ただループの代わりとして使っています。

そのループの中では、$/=get.words;.substr-rw($0-1..^$1).=flipを実行しています。

まず$/=get.words;で入力を1行読み込み、ABC097-Aと同じテクニックで$l$と$r$をそれぞれ$0$1でアクセスできるようにします。

.substr-rwは、メソッド呼び出しの省略形で$_に対して呼び出されているのですが、「変更可能な」部分文字列を返します。(普通のsubstrに代入しようとするとエラーが出ますが、これは代入できます。)
取り出す部分を指定する引数には、今回は範囲を渡すことにしました。0-indexedなので$l-1..r-1$という範囲を作っているのが$0-1..^$1です。
範囲の始点については$0-1と素直に1を引いていますが、範囲を作るのに..ではなく「範囲の終点を含まない」という意味の..^を使うことで、範囲の終点では1を引くのを省略しています。1Byteの短縮になります。

こうして得られた部分文字列を反転して取り出した部分に代入します。

反転するのにはflipというメソッドを使いますが、この「メソッドを呼び出し、その返り値を呼び出し元に代入する」という操作について考えます。
$invocant = $invocant.methodというのは、.を演算子としてみれば$a = $a+5と似た構造です。であれば、$a+=5のように$invocant.=methodと書けてもおかしくなさそうです。実際書けます。

$invocant.substr-rw($0-1..^$1)であり、methodflipです。くっつけて.substr-rw($0-1..^$1).=flipとなります。

こうして、ループの中では部分文字列を反転して代入する操作が行われています。これが$N$回繰り返されて、最後に$_を出力します。これも、say $_ではなく$_.sayと書くことで$_が省略できて、.sayで済みます。

Ruby(75Byte)
s=?1+gets;gets;$<.map{|i|s[x=eval(i.split*'..')]=s[x].reverse};$><<s[1..-1]

s=?1+getsで$S$を読み込み、?1は文字1を表すのでこれが先頭に追加され、sに代入されています。なぜ先頭に1文字追加しているかというと、文字列のほうを範囲指定に合わせて1-indexedにするためです。

getsで2行目を読み飛ばします。

$<に対してmapを呼び出すことで、残りの入力がすべて読まれて行ごとにブロックが実行されます。入力の行はiに代入されます。
ブロックが返す値には興味はなく、ただ短くかけるループとしてのみmapを使っています。

ブロック内ではs[x=eval(i.split*'..')]=s[x].reverseを実行しています。

まずi.split*'..'というのは、行を空白で分割して、'..'joinするという意味です。これによって、"l r"という入力が"l..r"という文字列になります。それをevalで評価すると範囲を表すオブジェクトになるので、インデックスにしてsにアクセスすれば指定された範囲が得られるというわけです。

もう1回使うのでxに代入しつつ、s[l..r]を得ます。Rubyでこうやって作った部分文字列は変更ができるので、ここにs[l..r].reverseを代入すれば部分文字列の反転ができました。

最後に出力をします。sの先頭1文字は必要ないので、s[1..-1]として求める箇所だけ取り出します。
$>というのは標準出力を表し、<<で文字列や数値を追加することで出力ができるので、$><<s[1..-1]となりました。空白の関係でputs s[1..-1]よりも1Byte短いです。
$><<を使うと改行が行われませんが、getsは改行付きで読み込むので、sの末尾には最初から改行がついており、問題ありません。

ABC019 高橋くんと文字列圧縮

$s$

文字列$s$中の1文字以上連続した同じ文字を、その文字+連続した文字数に置き換えて出力します。
aabbbaada2b3a2d1に変換されます。

Perl(33Byte)
print<>=~s/(.)\1*/$1.length$&/egr

置き換えるのですから、s/pattern/replacement/を使います。

まず、/pattern//(.)\1*/です。これは、まず1文字確定させて、それがさらに0文字以上続くというのにマッチングします。sedと違ってキャプチャにエスケープが必要ないので、()で囲むだけです。

replacementですが、マッチングした箇所の長さを埋め込む必要があります。これをするために、eフラグをs///の後ろにつけています。
eフラグがあると何をするかというと、replacementでそのまま置き換えるのではなく、replacementをPerlコードとして評価した結果を埋め込むようになります。("e"valするということです。)

replacementにおいて、キャプチャした文字は$1、マッチした部分文字列は$&で参照できます。$1length$&.演算子を使って文字列として連結し、埋め込みます。
この置換を文字列のすべての部分に対して行うので、gフラグもつけます。

<>=~として入力を読み込んだ直後に置換しているのですが、この<>は書き換え不可能なので、rフラグをつけることによって、実際に書き換えるのではなく置換した文字列を返すようにします。

以上3つのフラグを付けて、print<>=~s///egrとなります。<>で読み込んだ改行は.にマッチングせず、したがって変更されないので、末尾に改行がついたまま出力されます。

Bash(39Byte)
fold -1|uniq -c|awk '{printf$2$1}';echo

foldは行を折り返すコマンドです。-1とオプションを与えていますが、こうすると1文字ごとに折り返す、つまり、文字ごとに改行を挟むようにします。

uniqは「連続する」同じ行を1つにまとめます。(連続していない場合はまとめられません。)-cオプションを与えると、行の先頭に、まとめられた要素の個数を付けて出力します。

AWKから見ると、フィールド1に個数が、フィールド2にまとめられた行の内容があるので、これを逆にして連結し、出力します。余計なタブ文字や空白はフィールドの内容にはならないので、そういう余計なものを取り除く役割も持ちます。改行区切りにはなってほしくないので、AWKではprintfを使って改行せず出力しています。

ただ、こうしてしまうと末尾にも改行が入らないので、最後にechoを引数なしで呼び出すことで改行しています。

ABC020 足し算

$A \quad B$

$A$と$B$を文字列として連結し、数値として2倍して出力します。

Awk(11Byte)
$0=($1$2)*2

やるだけ。

17
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
7