10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-03-16

はじめに

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

言語別索引

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

Bash

ABC042 14Byte
ABC048 33Byte
ABC053 18Byte
ABC058 22Byte

Perl

ABC045 45Byte
ABC047 96Byte
ABC054 65Byte
ABC056 26Byte
ABC059 35Byte

ABC042
ABC046
ABC052
ABC055
ABC057

Ruby

ABC052 28Byte
ABC053 18Byte

ABC054
ABC057

Sed

ABC043 12Byte
ABC044 35Byte
ABC049 4Byte

Awk

ABC046 15Byte
ABC050 45Byte
ABC051 53Byte
ABC055 31Byte
ABC060 43Byte

ABC049
ABC056

Octave

ABC057 82Byte

ABC052
ABC058

Perl6

ABC041 27Byte

ABC052
ABC055
ABC058

問題

ABC041 直方体

$A \quad B \quad C$

$A \times B \times C \bmod{(10^9+7)}$を出力します。

Perl6(27Byte)
say (1e9+|7)R%[*] get.words

[*] get.wordsは入力を空白区切りにしたリストの総積です。

R%Rは2項演算子の項の順序を逆転させるメタ演算子で、a R% b == b%aとなります。
つまり、上のコードは([*] get.words)%(1e9+|7)と同じになり、1e9+|7 == 10**9+7であるため求める答えが得られます。

1e9+7とせず1e9+|7+|bitwise OR)を使っている理由は、1e9+7が小数のため、大きい数の余りを求めるときに精度の関係で答えが変わってしまうからです。
bit演算を使うと整数にキャストされるので1e9+|7は整数であり、こちらは問題ありません。

ABC042 文字列大好きいろはちゃんイージー / Iroha Loves Strings (ABC Edition)

$N \quad L$
$S_1$
$S_2$
$:$
$S_N$

$S_{1..N}$を辞書順でソートし、連結して出力します。

Bash(14Byte)
sort|tr -d -@

コマンドsortはデフォルトで辞書順比較をします。
1行目もまとめてソートし、最後にa-z以外の文字を全て削除すると、1行目だったものが消えるのと同時に不要な改行も削除されるので、連結したことになります。

コマンドtrのオプション-cは指定した文字集合の補集合をとるので、これでa-zの補集合を指定して削除する方法がありますが、最短コードでは削除する文字集合を直接指定しています。
削除する必要のある文字は、数字すべてと空白・改行文字です。削除する文字の範囲を指定するのですが、範囲の左端に、エスケープが必要なくASCIIコードで改行文字よりも小さい文字としてBack Space("\x08"または"\b")を直接書いています。
この文字はQiitaでは表示されないのですが、提出ページを見ると確認できます。

"\x08"-@という範囲はa-zを含まず数字すべてと空白・改行文字を含むので、これをオプション-dで削除すると、上で述べた操作ができます。

Perl(23Byte)
<>;print/./g for sort<>

1行目を読み飛ばします。
関数sortは比較関数を指定しないと辞書順比較になるので、これで残りの入力をソートし、ループを回します。

ループ内ではprint/./gを実行します。
/./g$_に対するマッチングであり、gオプションが適用されているので、リストコンテキストではマッチしたものすべてのリストを返します。
今回はprint関数の引数になっており、関数の引数はリストコンテキストのため、マッチしたものすべてのリストがprint関数に渡されて出力されることになります。
さらに/./は改行を除くすべての文字にマッチするため、見事改行だけを取り除くことができました。これで連結したことになります。

ABC043 バイナリハックイージー / Unhappy Hacking (ABC Edit)

$s$

文字列$s$を前から見ていき、文字'B'とその直前の文字(存在すれば)を削除します。このとき文字列は前に詰められます。
文字'B'が存在しなくなるまでこの操作を繰り返し、得られた$s$を出力します。

Sed(12Byte)
:;s/.\?B//;t

s/.\?B//が1回の削除に相当します。直前の文字が存在しない場合に備えて?(0個または1個を表す)がエスケープされて指定されています。

:はsedコマンドにラベルをつけます。tは直前のt以降でsコマンドによる置換が成功していた場合、:でラベル付けられた位置にジャンプします。
どちらもラベル文字列を指定することでジャンプ位置を区別しますが、このラベル文字列には空文字列が使えます。
これらのコマンドを使って、s/.\?B//を置換が行われる限りループします。

ABC044 美しい文字列 / Beautiful Strings

$w$

すべての英小文字が$w$に偶数回出現するならばYes、そうでなければNoを出力します。

Sed(35Byte)
:
s/\(.\)\(.*\)\1/\2/
t
/./cNo
cYes

s/\(.\)\(.*\)\1/\2/は、2つの同じ文字を削除します。\(.\)\1が削除される2つの同じ文字であり、その間にあった\(.*\)は置換後も\2として残るので、これで2つの文字だけを削除できます。

この操作を、ABC043-Bと同じくコマンド:tを使って2つの同じ文字が存在しなくなるまで行います。

こうして得られた文字列にまだ文字が存在すれば、その文字は元の$w$中に奇数回出現していたということなので、Noを出力します。この判定は/./で行えます。

ABC045 3人でカードゲームイージー / Card Game for Three (ABC Edit)

$S_A$
$S_B$
$S_C$

ゲームのシミュレーションをします。詳しくは問題文を読んでください。

Perl(45Byte)
$_=a;${$_++}=<>while?.?|$
$&=~s///;print uc$&

このコードの基本的な発想は、$S_A,S_B,S_C$をそれぞれ$a,$b,$cに代入した上で${現在のプレイヤー}=~s/.//で先頭文字を削除することにすると、$&(直前に.にマッチして削除された文字になる)が現在のプレイヤーを表す文字として使える、ということです。(s/.//.の最初の出現、つまり先頭文字のみを空文字列に置換します。)
これを実現するためには、まず最初に$&'a'である必要があります。

このことを念頭に置いて、順にコードを追っていきます。

まず$_に文字列"a"が代入されます。次にwhileループがありますが、これの条件部分は?.?|$ $&=~s///です。改行が挟まっている理由はABC032-Bの最後のほうに書いてあり、${$&}を1Byte短縮するためです。

?.?は特殊なマッチングを行います。ABC031-Bにも出てきましたが、これは最初の1回しかマッチングが成功しません。
${$&}=~s/.//ではなく${$&}=~s///になっているのは、Perlにおいて空のパターンは直前に成功したパターンに置き換わるからです。「'pattern' が空文字列なら、最後にマッチングに成功した正規表現が使われます」
?.?${$&}=~s/.//|bitwise ORしています。これは短絡しない論理和演算と見ることができ、||より1Byte短いです。

結局このwhileループは、まず最初に$_=~?.?$&'a'が設定され、それ以降は${$&}=~s/.//で先頭文字を削除しつつ$&を設定しなおしている、ということになります。

同時に、このループの${$_++}=<>で読み込みも行っています。最初に$_="a"としておいたので、ループが回るごとにマジカルインクリメントによって$a=<>$b=<>と順に読み込みが行われ、3回ループが回ると$S_C$まで読み込み終わります。そのあともPerlは読み込みを続けようとしますが、$a,$b,$cは変更されないので特に関係ありません。
読み込みタイミングの関係で、$S_A$の1文字目が'c'だった場合入力が全部読まれる前にループを抜けてしまいますが、幸い落ちるテストケースが存在しませんでした。

?.?が2回目以降は常にfalseなのを踏まえると、このループは${$&}=~s/.//falseになったとき抜けます。これは${$&}が空文字列であるということで、最初にこの状態になったときの$&がゲームの勝者です。$&は小文字なので、関数ucを挟んで大文字にして出力すればよいです。

ABC046 AtCoDeerくんとボール色塗り / Painting Balls with AtCoDeer

$N \quad K$

$K \times (K-1)^{N-1}$を出力します。

Awk(15Byte)
$0=$2--*$2^--$1

答えがint型に収まることが保証されているので、AWK(mawk)でも正しく計算できます。

Perl(25Byte)
print$'*($'-<>=~$")**~-$`

ABC014-Bと同じく、<>=~$"で入力を読み込みます。

式を前から読んでいくと、<>=~$"を実行する前に$'を使っていて答えが0になってしまうように見えますが、ここでABC006-Bで出てきた「左辺値」がうまく働いています。
$'*($'-<>=~$")を評価するとき$'の値は取り出されないまま進んでいき、最後に<>=~$"を評価したあと$'の値も取り出されるのですが、このタイミングではすでに$'の値が設定されているので正しく動きます。

<>=~$"のマッチングは成功するので、数値としては1になり、これで$K-1$を作っています。
$K-1$の$N-1$乗を計算するのですが、$N-1$のほうはbit演算のテクニック~-a==a-1を使っています。これで求める答えが計算できました。

Perlでのbit演算による+1-1には注意が必要です。負の数のbit反転はC言語などと同じように計算されるので、正の数に対する~-aや負の数に対する-~aは意図したとおりに動きますが、正の数のbit反転は負の数にはならずとても大きな値になってしまうので、うまく+1-1をすることができません。
Perlでは、bit演算の結果は符号なし64bit整数にキャストされるので、2の補数表現であらわされた負の数は最上位bitが1の巨大な数になってしまうからです。

ABC047 すぬけ君の塗り絵 2 イージー / Snuke's Coloring 2 (ABC Edit)

$W \quad H \quad N$
$x_1 \quad y_1 \quad a_1$
$x_2 \quad y_2 \quad a_2$
$:$
$x_N \quad y_N \quad a_N$

${\rm max}({\rm min}(\{x_i|a_i=2\}\cup\{W\})-{\rm max}(\{x_i|a_i=1\}\cup\{0\}),0)\times{\rm max}({\rm min}(\{y_i|a_i=4\}\cup\{H\})-{\rm max}(\{y_i|a_i=3\}\cup\{0\}),0)$を出力します。

Perl(97Byte)
($s,$u)=glob<>;/ .* /,${*t=$'&w^A}-=$-=$t+(-$&,$&,-$`,$`)[-$']for<>;print-($-=$s+$p)*-($-=$u+$r)

${\rm min}$と${\rm max}$が混じっていて扱い辛いので、${\rm max}$のときは符号を逆にすることですべて${\rm min}$に統一します。
a=min(a,b)a-=max(a-b,0)は等価です。このmax(何か,0)という形はABC024-Bで出てきたように、組み込み変数$-を使用して短く書くことができます。
Perlは標準ではmin関数を使えないので、$a-=$-=$a-$bと書くのが最も短くなります。

$a_i$の値によって${\rm min}$を保存する変数を変える方法を考えます。上のコードでは、文字'1'..'4'と文字'A'XORを変数名にしています。Perlでprint"1234"^"AAAA"を実行してみるとわかりますが、順に'p','s','r','u'になって区別することができます。

$a_i=2$と$a_i=4$のときの変数はそれぞれ$W,H$で初期化する必要があります。変数名's''u'に対応するので、($s,$u)=glob<>で1行目の読み込みと同時に代入しておきます。$N$は代入先がないため捨てられています。

以降の入力でループを回します。毎回最初に$_=~/ .* /を実行して、ABC037-Bのように$`,$&,$'を設定します。

次に、$a_i$の値によって変数名を計算します。$a_i$は$'に相当するので$'^'A'とすれば良いように思えますがこれは罠で、$'は末尾に改行がついているため、"s\n"となってしまいます。$'の改行を無視するためには、いったん文字'w'ANDをとるとよいです。
なぜこうして解決できるかというと、ORXORでは計算後の文字列の長さを長いほうの文字列に合わせるのに対して、ANDは短いほうの文字列に合わせるからです。長さ2の文字列$'と長さ1の文字列'w'ANDをとると、長さ1の文字列が得られます。
("1234"&"wwww")eq "1234"なので、'w'ANDをとることによって$a_i$の値が変わったりはしません。
文字列のXOR等を計算するためには、項の両方が文字列である必要があるため、$'+0と数値にして改行を取る方法は使えません。

'w''A'は裸の単語機能を使うことにして、$'&w^Aで変数名を計算することができました。これを*tに代入しておきます。
*t型グロブというもので、例えば*t='p'とすると$t$pの別名になります。(@t@pの別名になったりもするのですが、今回は関係ありません。)
よって$tだけで計算した変数名の変数にアクセスできるようになります。

${\rm min}$を計算するもう片方の変数は、$a_i$の値の順に$-x_i,x_i,-y_i,y_i$となっています。こちらは配列を使ってアクセスすることにして、(0,-$`,$`,-$&,$&)[$']と書けます。配列を逆に並べて負のインデックスを使うことで配列長を5から4にすることができ、そのほうが短くなるので、($&,-$&,$`,-$`)[-$']を使うことにします。

これでやっと主要な計算部分が構成できました。$t-=$-=$t-($&,-$&,$`,-$`)[-$']です。*tへの代入を詰め込むため、最初の$t${*t=$'&w^A}としています。また、上のコードでは符号を少し入れ替えています。どちらもコード長は変わりませんでした。

最後に出力を行います。ここでも${\rm max}$が出てきているため、変数$-を活用します。
${\rm min}(\{x_i|a_i=2\}\cup\{W\})-{\rm max}(\{x_i|a_i=1\}\cup\{0\})$は$s-(-$p)に、もう片方は$u-(-$r)に対応します。これをいったん$-に代入して0との${\rm max}$をとってから掛け合わせればよいです。

ところが、単に($-=$s+$p)*($-=$u+$r)とだけ書くと、左の$-が左辺値であるため値が変更されてしまいうまく計算できません。左辺値でなくすればよいので、どちらも符号を逆転させて掛け合わせることにします。つまり、-($-=$s+$p)*-($-=$u+$r)です。これは、print関数の直後に括弧が来ないようにする役割も併せ持ちます。

ABC048 Between a and b ...

$a \quad b \quad x$

$\lfloor \frac{b}{x} \rfloor - \lceil \frac{a}{x} \rceil+1$を出力します。

Bash(33Byte)
read A B C;ruby -ep$B/$C-~-$A/$C

入力される数値が大きいので、精度を気にする必要があるAWKでは簡単にはACできません。出力や切り捨てが簡単に行えるRubyを使うことにして、入力をBashで肩代わりするコードが最短になりました。
こうやって、入力部分が長い言語をBashから呼び出すことで短縮するテクニックがあります。

まずRubyコードを読みます。$\lfloor \frac{b}{x} \rfloor - \lceil \frac{a}{x} \rceil+1=\lfloor \frac{b}{x} \rfloor - \lfloor\frac{a+x-1}{x} \rfloor+1=\lfloor \frac{b}{x} \rfloor - \lfloor \frac{a-1}{x} \rfloor$と式変形します。
a-1==~-aなので、b/x-~-a/xで答えが得られます。

Rubyの関数pと引数の間には空白が必要なので、本来であればruby -e"p $B/$C-~-$A/$C"としたり、またはruby -ep\ $B/$C-~-$A/$Cと空白だけをエスケープする必要があります。上のコードではp$Bの間に何もないように見えますが、どうなっているのでしょうか?

実は、エスケープのいらない空白に類する文字として"\x0c"(改ページ)が挿入されています。提出結果の画面には表示されず、こうしてQiita記事にコピーしてきても貼り付けられませんでした(実際、上のコードのByte数を数えると32になっています)が、メモ帳に貼り付けてみると確認できます。

ABC049 たてなが / Thin

$H \quad W$
$C_{1,1}...C_{1,W}$
$:$
$C_{H,1}...C_{H,W}$

2行目以降を2回ずつ出力します。

Sed(4Byte)
1d;p

1行目を削除します。pコマンドはデフォルトの出力とは別に現在のパターンスペースを出力するので、これでデフォルトの出力と合わせて2回出力することになります。

とてつもなく短いです。ABC121まで開催された執筆時現在において、ABCの全提出の中でABC007-Aと並んで最短のACコードです。
世界は広いので、ABCに制限しないならば1ByteのACコードも存在します。マラソンで何もしない場合や、これです。

Awk(13Byte)
a++,$0=$0RS$0

RSのデフォルトは改行文字なので、$0=$0RS$0で改行を挟んで2回繰り返した文字列になります。
1行目の読み飛ばしはABC004-Aでも使用した範囲パターンを使っています。
ABC004-Aでは常に出力するために使用しましたが、こちらではカウント変数をインクリメントし、a==0が成り立つ入力の1行目のときだけfalseになって出力されないようにしています。

ABC050 Contest with Drinks Easy

$N$
$T_1 \quad T_2 \quad ... \quad T_N$
$M$
$P_1 \quad X_1$
$P_2 \quad X_2$
$:$
$P_M \quad X_M$

$\sum_{j=1}^{N}T_j-T_{P_i}+X_i$をすべての$i$について出力します。

Awk(45Byte)
NR~2{for(;$++i;s+=T[i]=$i)}NR>3,$0=s-T[$1]+$2

2行目を処理するときにfor(;$++i;s+=T[i]=$i)を実行します。すべての$i$について$T_i>0$なので、$++i++i<=Nであるあいだtrueになり、NFを使うことなくフィールド変数全体のループを回すことができます。
ループ内ではsに$T_i$の総和を計算しつつ、個々の$T_i$を配列に保存しています。

2行目を処理するときだけこのループを回したいので、ブロックにつける条件は本来ならNR==2とするべきなのですが、かわりにNR~2を条件としています。
これはNRを文字列としてみたときに'2'が含まれるか?ということを調べるマッチングで、NR==2に加えてNR=12NR==24などでもマッチに成功してしまいます。
ですが、ブロックに2回以上入るときは必ず++i>2になっており、$3以降が存在する可能性があるのは入力の2行目だけなので、NR==12などではループは1度も回ることなく終了します。よって、NR~2でも問題ありません。

範囲パターンを使い、NR>3が成り立つ場合にs-T[$1]+$2を出力します。これは各$i$についての$\sum_{j=1}^{N}T_j-T_{P_i}+X_i$に相当します。

ABC051 Sum of Three Integers

$K \quad S$

$X+Y+Z=S$を満たす$0 \le X,Y,Z \le K$の組み合わせの個数を出力します。

Awk(53Byte)
{for(;i<=$1;s+=(0<=b=a>$1?$1*2-a:a)*++b)a=$2-i++}$0=s

i<=$1で$0 \le X \le K$を全探索します。$X$がiになります。a=$2-i++としているので、$Y+Z$がaになるような値の組を数えればよいです。ここで場合分けをします。

  • $a \gt K$のとき
    $(Y,Z)=(a-K,K),(a-K+1,K-1),...,(K,a-K)$より$K-(a-K)+1=2K-a+1$通り
  • $a \le K$のとき
    $(Y,Z)=(0,a),(1,a-1),...,(a,0)$より$a+1$通り

よって、b=a>$1?$1*2-a:aとしておくとb+1が求める組の個数になります。

実際には、$a \lt 0 \lor a \gt 2K$のときは0通りにならなければなりません。このとき0>bとなっているはずなので、(0<=b)*(b+1)とすれば場合分けをせずに書けます。

代入文と比較をまとめ、+1をするときにインクリメントを使ったりすることで上のコードが完成します。変数sに組の個数を足していって、最後に$0=sで出力を行います。制約から答えは必ず正の数になるので、$0trueになります。

ABC052 Increment Decrement

$N$
$S$

文字列$S$を先頭から見て、「それまでに出現した'I'の個数-それまでに出現した'D'の個数」の最大値を出力します。

Ruby(28Byte)
p`sed ':;s/DI//;t'`.count ?I

文字列$S$に"DI"という部分文字列が存在する限り、それを削除して切り詰めることを繰り返します。この操作の前後で答えは変わらないため、操作が終了して"II...ID...DD"という形になった文字列$S$の'I'の個数を数えて出力すればよいです。

"DI"が存在する限り削除する、という操作はsedを使ってABC043-Bのように行えます。そうして得られた文字列の'I'の個数をcountで数え、出力します。入力の1行目に文字'I'は含まれないため、わざわざ読み飛ばしをする必要はありません。

Perl(30Byte)
# !perl -p0
$_=s;DI;;?redo:y;I;

1行目の#!から始まる行はshebangといいます。Perlスクリプトの実行時にコマンドラインで指定するスイッチを、スクリプトから指定するために使用しています。(スイッチとはBashコマンドに対するオプションのようなものです。)
与えるスイッチは2つ、-p-0です。(各スイッチの説明はコマンドスイッチにあります。)

-pは、以下のようなループをスクリプトに追加します。

LINE:while(<>){
    元のスクリプト;
}continue{
    print or die "-p destination: $!\n";
}

1行読み込むごとに、それを$_に代入して元のスクリプトを実行した後$_を出力するようになります。
これはwhile(<>)while(defined($_=<>))が等価であることと、print関数に引数を渡さなかった場合$_が出力されることによります。(variable $_

-0は入力を区切る文字$/を設定します。後ろに数値が続いていればその文字コードの値になりますが、今回は何も続いていないのでヌル文字となり、入力を一度にすべて読みます。

スクリプトの2行目に進みます。$_=s;DI;;?redo:y;I;ですが、先ほど見たようにこのスクリプトの直後に;が追加されるので、$_=s;DI;;?redo:y;I;;となります。

まず$_=~s/DI//が実行されます。この/は元のスクリプトでは;になっていますが、意味に違いはありません。もし"DI"という部分文字列が存在したら、それを削除した後三項演算子の2項目redoに処理が移り、whileループがもう一度やり直されます。

"DI"が存在しなかった場合、y;I;;に処理が移ります。こちらも先ほどのs;DI;;と同じようにy/I///;になっているだけです。$_に含まれるIの値が返り、$_に代入されます。この値は出力すべき答えです。whileループの1回の処理が終わり、$_が出力されます。

入力を一度にすべて読むよう指定していたので、出力も1回しか行われません。これでACできます。

-pスイッチを用いたとき末尾に;が追加されることを利用してy/I//y;I;と書くと、1Byteの短縮になります。これは非常に面白いテクニックです。

Perl6(38Byte)
get;say max [\+] 0,|map 4-*%7,get.ords

(上のRubyやPerlと同じ方法で求めたほうが短くなりますが、あえて以前まで最短であった解法のものを載せています。)
メソッドordsは文字列の各文字の文字コードをリストにまとめます。
'I'の文字コードは73'D'の文字コードは68なので、これを+1/-1に変換して累積和を求めるとよさそうです。具体的には、4から7で割ったあまりを引けばよいです。4-73%7==4-3==14-68%7==4-5==-1なのでうまく変換できています。
それを行っているのがmap 4-*%7,get.ordsの部分で、ABC026-Bと同じくWhateverCodeを使っています。4-*%7が、*に各文字コードが入る関数になっているということです。

こうして得られた+1/-1のリストと、最初の値0をまとめて累積和を計算します。[+]とするとリストの総和になりますが、ここで[\+]とバックスラッシュを入れると計算途中の値をすべてリストにして返してくれるので、そのmaxを出力すればよいです。

注意点として、mapのほうはすでに1つのリストにまとまっているので、|を前置して平坦化しておかないと(0,+1,-1,...)ではなく(0,(+1,-1,...))の累積和を求めることになってしまい、正しく動きません。

Octave(44Byte)
disp(max([0,cumsum(-(-1).^scanf("%*s%s"))]))

cumsummaxを使っており、解法はPerl6とほとんど変わりません。累積和をとってからmaxに渡す前に0を付け加えているのも一緒です。
違うのは、+1/-1を作り出す方法です。

scanfで入力を読み込むのですが、1行目は必要ありません。C言語と同じく、フォーマット文字列で'*'を挟んでおくとそれに相当する入力が読み捨てられるので、この機能を使います。"%*s%s"とあるので、1行目が読み捨てられます。

こうして得られた文字列ですが、Octaveでは文字列は文字コードのベクトルですから、数値としても扱えます。-1を文字コード乗すると、偶奇によって+1/-1に分かれます。'I'==73のとき+1'D'==68のとき-1が欲しいので、-(-1)^文字コードとすれば正しく変換することができます。ベクトルの「要素ごとの演算」なので、演算子は^ではなく.^を使う必要があります。

ABC053 A to Z String

$s$

$s$の部分文字列で、先頭が'A'で末尾が'Z'のもの長さの最大値を出力します。

Bash(18Byte)
grep -o A.*Z|wc -L

grepのオプション-oは、行単位ではなくマッチする部分のみを出力するようにします。
'A'から始まって'Z'で終わる部分文字列は複数存在する可能性がありますが、A.*Zにマッチする部分文字列は1つのみです。なぜなら、最初の'A'の出現から最後の'Z'の出現までの間の文字がすべて.*に吸収されるからです。

こうして得られた文字列の長さを求めるには、wc-Lオプションを使用します。文字数を数えるオプション-cでは改行文字のぶん答えが1大きくなってしまいますが、-Lは文字列を表示するときの「幅」を出力してくれます。この「幅」は文字列の長さに相当するので、これが答えです。

Ruby(18Byte)
p`dd`[/A.*Z/].size

文字列のインデックスに正規表現を用いると、マッチする部分文字列があればそれが、なければnilが得られます。/A.*Z/にマッチする部分文字列が存在することは保証されているので、その文字列長を求めて出力すればよいです。
getsではなく`dd`を使うことでpとの間の空白を取り除くことができます。

ABC054 Template Matching

$N \quad M$
$A_1$
$A_2$
$:$
$A_N$
$B_1$
$B_2$
$:$
$B_M$

$A$と$B$の模様を重ねられればYes、できなければNoを出力します。

Perl(65Byte)
$A.=<>for<>=~$"..$`;$.-=$';print$A=~(`dd`=~s/
/\\D{$.}/gr)?Yes:No

$A$をすべて連結した文字列$Aに、$B$の改行を\D{N+1-M}N,Mは入力の数値、{数値}は直前の要素を数値回繰り返すことを表す)に置換した文字列がマッチすればYes、しなければNoを出力すればACできます。テストケースがかなりガバガバですが、とりあえずコードを先に読みます。

まず$N$回ループを回して$Aを作ります。このループには<>=~$"..$` を使います。<>=~$"が$1$、$` が$N$なので、これで1..Nの範囲を作ることができます。ループ内では単純に$A.=<>とし、1行読んで連結しています。

ここで$N+1-M$を求めます。これまでに読み込んだ行数はちょうど$N+1$行であり、組み込み変数$.に保存されています。そこから$M$、つまり$'を引けば$.の値は$N+1-M$になります。

次に$B$を加工します。`dd`で残りすべての入力を読み、すべての改行を\D{$.}に置換します。
s/pattern/replacement/ではpatternreplacementの部分がクオートされた文字列として扱われるので、\にはエスケープが必要であり、また$.は$N+1-M$に展開されます。
`dd`は書き換え不可能な文字列なので、rフラグをつけて元データの書き換えをせず、置換した後の文字列を返すようにしておきます。
$Aをこの文字列にマッチさせるのですが、=~の結合順序が左からであるため、括弧が必要です。

さて、ではどういうマッチングが行われているのか確認していきます。
abc
def
ghi

de
gh
を重ねることを考えます。(アルファベット文字列にしたのはわかりやすさのためです。)
この場合、$A"abc\ndef\nghi"になっています。$.==2なので、これに"de\D{2}gh\D{2}"をマッチさせます。\Dは数字以外の任意の1文字を表します。
実際に$A"def\nghi\n"の部分にマッチするので、めでたくYesが出力されます。

"de\D{2}"が全体で元の$A$の1行分の長さになっているため正しくマッチングできています。$B$の右端の列の文字と左端の列の文字(例えば上の例で'e''g')の間には、$A$において改行込みで$N+1-M$文字存在するべきである、という考え方です。「改行込み」を実現するために、デフォルトでは改行にマッチしない.ではなくマッチする\Dを使用しています。

なぜテストケースがガバガバと言ったのか説明します。
まず、$_の末尾にも\D{$.}が入ってしまうので、$Aの下1行ではマッチングが成功しません。それ以降に$N+1-M$文字も存在しないからです。
さらに、元の模様はアルファベットではなく'#''.'でした。この'.'という文字は正規表現において「改行以外の任意の1文字」を表す特殊な文字なので、完全に一致することを調べているわけではありません。極端な話をすると、$B$がすべて'.'であるような模様は、いかなる$A$に対してもマッチが成功してしまいYesが出力されます。これは明らかに正しくないです。

Ruby(92Byte)
n,m,*A=`dd`.split;B=A.pop m=m.to_i;puts (0..49).any?{|i|(A.map{|a|a[i,m]}*n)[B*n]}?:Yes: :No

`dd`.splitで入力を全部読み、空白と改行で区切ります。この状態では$A$と$B$を区別できないので、B=A.pop(m)Aの後ろから$M$要素を抜き出し、Bに代入します。同時に、mは文字列なので、数値に変換して代入しなおしています。まとめるとB=A.pop(m=m.to_i)となり、この括弧は省けるので上のコードが得られます。

$A$の左から何列目に$B$が重なるかを全探索します。i列目については、Aの要素それぞれのi文字目からm文字を抜き出し、適当な区切り文字で連結したものに、Bを同じ区切り文字で連結したものがマッチするかどうかを調べればよいです。

"文字列"[i,m]i文字目からm文字の部分文字列になるので、A.mapでこれをAのすべての要素について求めます。区切り文字には$A$にも$B$にも出現しない文字を使いたいですが、ちょうどnが文字列のまま残っていて、数字だけで構成されているため条件を満たします。配列の要素を文字列として連結するには演算子*を使います。

まとめると、A.map{|a|a[i,m]}*nB*nがマッチするかを調べればよく、(A.map{|a|a[i,m]}*n)[B*n]でできます。
マッチという言葉を使いましたが、別に正規表現の機能を使うわけではないので、正確にはABC003-Bで用いたような部分文字列の取り出し機能を使っています。そのままtruefalseにするのも同じです。

うまくマッチできるかをany?で全探索します。調べる範囲について、iは$0$から$N-1$まで動く必要がありますが、$N$以上になっても問題ないため、最大値である49を指定しておきます。

0..49の中でマッチできたものが1つでも存在すればYesを、存在しなければNoを出力します。出力する文字列はシンボルオブジェクトを使って:Yes:Noと書けます。三項演算子のパースの関係で1文字空白が必要ですが、クオートするより短くなります。

ABC055 Training Camp

$N$

$N!\bmod{(10^9+7)}$を出力します。

Awk(31Byte)
{for(n=$1;--n;$0%=1e9+7)$0*=n}1

$0に$N-1$から$1$までを掛け算します。最初に変数n$1を代入しておくことで、$0を変更しても問題ないようにしています。
掛け算と余りをとる操作を分けて書くことで、1e9+7を括弧でくくる必要がなくなります。
ブロックを抜けた後1を評価して出力します。

Perl(32Byte)
($.*=$_)%=1e9+7for 1..<>;print$.

1..<>で$N$回ループを回します。読み込むのは1行だけなので、読み込んだ行数をカウントする組み込み変数$.は初期化せずに1として使える変数となります。これに$_を掛け、さらに1e9+7で割っています。AWKと同じく、掛け算と余りをとる操作を分けることで括弧を省いています。数字とforは間に空白がなくても正しくパースされます。

Perl6(33Byte)
.++;(($_*=++$)%=1e9+7)xx get;.say

$_にかけ合わせていくことにします。$_の初期値は(Any)==0であり、$_.sum.sumと省略できたように$_++.++と書けるため、初期化は$_=1よりインクリメントしたほうが短くなります。出力もsay $_$_.sayではなく.sayだけで済みます。

ABC018-Bと同じように、xxを使って(($_*=++$)%=1e9+7)get回ループします。

各ループでは++$を掛けた後1e9+7で割ったあまりを求めています。これもAWKやPerlと同じく括弧を省けるように操作を分けています。
$ABC028-Bでも使った無名の変数です。ループの最中は同じ変数がインクリメントされ続けるので、順に$1,2,...,N$と変化していき、掛け合わせることで階乗が得られます。

ABC056 NarrowRectanglesEasy

$W \quad a \quad b$

${\rm max}(|a-b|-W,0)$を出力します。

Perl(26Byte)
$/=$";print$-=-<>+abs<>-<>

max(何か,0)なので$-を使います。ABC030-Aでも使ったテクニックである$/=$"を実行して、読み込みの区切り文字を空白文字に設定しておきます。
$W$が先に読み込まれることに注意して、$-W+|a-b|$となるようにコードを書けばよいです。absの関数呼び出しの括弧は、absが式の最後にあるので省けます。

Awk(28Byte)
1,$0*=0<$0=(($2-$3)^2)^.5-$1

範囲パターンを利用して常に出力が行われるようにしています。
まず$0に$|a-b|-W$を代入します。absを求めるには、2乗したあと0.5乗すればよいです。
$0>0の場合はそのまま出力すればよいです。$0<=0の場合は$0=0にする必要がありますが、この場合分けは$0*=0<$0で書けます。$0<=0のときは$0*=0が実行されるので、$0=0と同じ効果があります。

比較と代入を同時に書くことで上のコードになります。

ABC057 Checkpoints

$N \quad M$
$a_1 \quad b_1$
$:$
$a_N \quad b_N$
$c_1 \quad d_1$
$:$
$c_M \quad d_M$

各$i \in \{1..N\}$について、$|a_i-c_j|+|b_i-d_j|$の最小値をとる$j$を出力します。

Octave(82Byte)
a=dlmread(0)';for k=1:a(1)[_,i]=min(sum(abs(a(:,k+1)-a(:,a(1)+2:end))));disp(i)end

dlmreadで、入力をそのまま行列として読み込みます。$N+M+1$行$2$列の行列になり、転置してaに代入します。

forループで各$i$について答えを求め、出力します。$N$はa(1)に入っているので、ループを回す範囲は1:a(1)と書けます。ループ変数はkになっています。for k=1:a(1)[_,i]=...の間にセミコロンは必要ありません。

$|a_i-c_j|+|b_i-d_j|$をすべての$j$について一度に求めます。
${}^t\left( \begin{array}{} a_i & b_i \end{array} \right)$にはa(:,k+1)でアクセスでき(1列目には$N$と$M$が入っています)、${}^t\left( \begin{array}{} c_j & d_j \end{array} \right)$はa(:,a(1)+2)からa(:,end)に入っています。
よって、a(:,k+1)-a(:,a(1)+2:end)とすると各$j$について${}^t\left( \begin{array}{} a_i-c_j & b_i-d_j \end{array} \right)$が$2$行$M$列の行列として求められるので、これの絶対値の和を各列について求めればよいです。
abssumを使うのですが、このsumはデフォルトで各列の和を求めます。ここで和を求める方向を指定する手間を省くために、最初に読み込んだときに転置してaに代入しました。

こうして得られた$M$要素のベクトルの最小値をmin関数で求めるのですが、[_,i]=minとすると2つの返り値を受け取ることができます。min関数は2つ目の返り値が要求された場合、最小値をとるインデックスを一緒に返してくれるので、これをiに受け取って出力します。Octaveは1-indexedなので、これでACできます。

Ruby(106Byte)
(n,),*a=$<.map{|s|s.split.map &:to_i};a.shift(n).map{|x,y|p 1+a.index(a.min_by{|z,w|(x-z).abs+(y-w).abs})}

$<.mapを使うと入力のすべての行を読んでブロックを実行できます。ブロック内では空白区切りにして数値に変換しており、これを(n,),*aに代入します。
1行目は(n,)に、残りはaに割り当てられ、さらに1行目の$M$は相当する変数が指定されていないため捨てられます。

a.shift(n)aの前から$N$要素、つまり$a_i$と$b_i$が得られ、この各ペアについてブロックを実行しています。shiftを実行すると配列が破壊的に変更されるので、この時点でaには$c_j$と$d_j$のペアしか入っていません。

各ブロックでは、min_byを使うことで$|a_i-c_j|+|b_i-d_j|$の最小値を与える$c_j$と$d_j$のペアを求めています。知りたいのは$j$なので、indexで最小値を与えるペアのaでの位置を調べます。Rubyは0-indexedなので1を足して出力すればよいです。

Perl(113Byte)
$m=1e9,print/ /*(grep{($c,$d)=glob$c[$_-1];$m-=$-=$m-abs($c-$`)-abs$d-$';$-}1..push@c,<>)[-1],$/for map~~<>,1..<>

map~~<>,1..<>で$2$行目から$N+1$行目までのリストを得て、ループを回します。ここでわざわざ~~<>とbit反転を2回しているのは、単に<>と書くとmapによるリストコンテキストで評価されてしまい、入力が1度に全部読まれてしまうからです。

ループ内を読みます。
最小値を保持する変数$mを大きな値で初期化しておきます。次に/ /を実行して$` $'を設定します。
grepを実行し、得られたリストの最後の値((grep)[-1])に/ /による1を掛け、改行と一緒に出力します。
わざわざ/ /を掛けているのは、print(grep...の間を離すためです。

1..push@c,<>grepのループを回します。push@c,<>では1度目の実行で残りの入力すべて(push関数の引数はリストコンテキストです)が@cに代入されますが、2度目以降は<>が何も読み込めないので@cは変化しません。push関数は更新後のリストの長さを返すので、何度目の実行であっても1..push@c,<>は$1..M$を意味します。これを@cのインデックスとして、最後に最小値が更新されたときの値を出力すればよいです。

grepのブロック内では、まず$c$dを設定します。1-indexedから0-indexedに直すために1引いて、$c[$_-1]globで分割します。
このとき、$|a_i-c_j|+|b_i-d_j|$はabs($c-$`)+abs$d-$'と書けます。minを求めるので$-を使って計算します。最小値が更新された場合、$-は正の値になっています。つまり、$-をブロックの最後において真偽値として使うと、最小値が更新されたときのインデックスだけを抜き出すことができます。
求める答えはこうして抜き出したインデックスの最後の要素であり、(grep)[-1]で得られるので、先に見たようにこれを出力していました。

ABC058 ∵∴∵

$O$
$E$

$O$の文字と$E$の文字を先頭から交互にすべて出力します。

Bash(22Byte)
perl -pes/.\\K/getc/ge

ABC052-Bと同じく-pスイッチを指定しています。shebangを使うよりもBashからPerlを呼び出して、その時にコマンドラインオプションとして渡すほうが短くなります。
実行するスクリプトは-eの後に続く文字列s/.\K/getc/geです。(\にはエスケープが必要なので、2つ重なっていました。)

-pスイッチによって、まず1行読まれて$_に代入されます。

パターン文字列/.\K/を読みます。
\Kとは、その左にあるパターンを$&に含めないマッチングを表し、何かの直後に続く文字列を指定するときに使います。(拡張パターン
/.\K/で「任意の1文字に続く空文字列」を表しており、gフラグの効果により1文字目の直後、2文字目の直後、……の空文字列にマッチします。

eフラグが指定されているため、それぞれがgetcを実行した結果に置換されます。空文字列を置換することはその位置に挿入することと等価です。getcは1文字読み込む関数なので、結局s/.\K/getc/geで「1行目の各文字の直後に2行目の各文字を順番に挿入する」ことになります。これは$O$と$E$が交互に現れる文字列に外ならず、-pスイッチの効果で出力されます。

$|O|=|E|$であるとき、入力の2行目には改行が残り、ループがもう一度回ります。
("abc\n","def\n")->("adbecf\n","\n")
$|O|=|E|+1$であるとき、1行目の出力の末尾には改行が2文字存在し、入力の2行目には何も残りません。
("abcd\n","efg\n")->("aebfcgd\n\n","")
どちらにせよ出力の末尾には改行が2文字存在することになりますが、問題なくACできます。

Perl6(27Byte)
say |[Z~] (|get.comb,@)xx 2

$|O|=|E|$のとき、文字列連結の演算子~zipするメタ演算子Zを使ってget.comb Z~ get.combとすれば2文字の文字列のリストが得られます。これを連結して出力するには、ABC025-Aのように|を前置して平坦化すればよいです。

$|O|=|E|+1$のときは少し厄介で、そのままではzipするときにリストの短いほうに合わせられてしまい、$O$の最後の文字が出力されません。これを防ぐためには、(|get.comb,'')と空文字列でリストの長さを増やしておくのがよいです。(get.combを平坦化する必要があります。)
実は、演算子~の適用時に自動で文字列に評価されるので、空文字列そのものではなく、評価されると空文字列になるものでも構いません。そういう値で''より短く書けるものを探すと、$@などの無名変数が使えます。上のコードでは@というリストの無名変数を使っています。

(|get.comb,@)を2回書くより、xxを使ってリストのリストを作り[Z~]foldしたほうが短くなります。
出力の際に平坦化するときも余計な括弧が必要なくなりました。

Octave(32Byte)
printf("%s",[fgets(0);fgets(0)])

fgets(0)で1行読み込むと、要素が文字の$1$行$|O|+1$列行列(改行のぶん1文字増えます)が得られます。2回繰り返し、$2$行$|O|+1$列行列にまとめます。$|O|>|E|$のときは足りない分を自動的に空白で埋めてくれます。

この行列をprintfで出力すると要素を列ごとに見てくれるので、これで交互に出力できます。フォーマット指定の文字列に"%s"とだけ書いているので、間に余計な空白や改行も入りません。末尾には空白や改行が出力されますが、問題なくACできます。

ABC059 Comparison

$A$
$B$

$A \gt B$のときGREATER、$A \lt B$のときLESS、$A=B$のときEQUALを出力します。

Perl(35Byte)
print+(EQUAL,GREATER,LESS)[<><=><>]

もう見た:ABC030-AABC054-AABC083-A

まじめな話をします。本来この問題では入力される数がとても大きいため、use bigintとして多倍長整数を使うようにしなければ解けないはずでしたが、$A$と$B$の差が小さいテストケースが存在しないため精度を気にせずそのままACできました。

ABC060 Choose Integers

$A \quad B \quad C$

$A \times i \equiv C \pmod{B}$となる$i \gt 0$が存在すればYES、しなければNOを出力します。

Awk(43Byte)
{for(i=$2;--i&&i*$1%$2-$3;)}$0=i?"YES":"NO"

$0 \lt i \le B$の範囲を探索すればよいです。テストケースハックをすると、$0 \lt i \lt B$だけでよいことがわかります。
--i&&i*$1%$2-$3falseになってループを抜けたとき、$C \lt B$なので$i=0 \lor A \times i \equiv C \pmod{B}$が成り立ちます。$i=0$のときは$A \times i \equiv C \pmod{B}$であってもNOを出力するので、i?"YES":"NO"で判定できます。

10
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
10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?