−目次
CODE FESTIVAL 2017 Qual C
B - Similar Arrays
B: Similar Arrays - CODE FESTIVAL 2017 qual C | AtCoder
問題:数列Aに「似ている」数列の中で、各項を全て掛け合わせると偶数になるようなのはいくつ?(「似ている」の定義は問題文参照)
- 各Aiにつき、取り得るBiは3通り(Ai-1, Ai, Ai+1)
- Bの全項の積が偶数になる数=Bの項に偶数が1つでもある数=(全体の数)ー(Bの項が全て奇数の数)
- Aiが奇数の時、(Ai-1, Ai, Ai+1)の中で奇数はAiのみ
- Aiが偶数の時、(Ai-1, Ai, Ai+1)の中で奇数はAi-1, Ai+1の2通り
- 3N−2Aに含まれる偶数の数
C - Inserting 'x'
C: Inserting 'x' - CODE FESTIVAL 2017 qual C | AtCoder
問題:英小文字からなる文字列sの好きな位置に'x'を挿入して回文にできるか?できるなら何回挿入すればいい?
sを前後双方から1文字ずつ見ていって、同じか調べる
- 両方同じなら両方次の文字へ
- 片方が'x'なら、そうで無い方に'x'を挿入。'x'である方を次の文字へ
- 両方'x'以外で異なるなら、回文にできない
- 前からの探索と後ろからの探索がぶつかったら終わり
D - Yet Another Palindrome Partitioning
D: Yet Another Palindrome Partitioning - CODE FESTIVAL 2017 qual C | AtCoder
問題:英小文字からなる文字列sを分割する。分割された各項は、並べ替えると回文が作れるようになっていなくてはならない。最小の分割数を求める。
- 文字列tを並び替えると回文が作れる=tに含まれる文字ごとに出現回数をカウントすると、奇数個含まれる文字が0~1個であり、他の文字は全て偶数個である。
文字は英小文字のみという制約があるので、26種類。奇数か偶数かだけ分かればいいので、ビットフラグが使える。'a'=0x001, 'b'=0x010, 'c'=0x100, …とする。
ある文字列sに含まれる文字が奇数個ならビットを立てる。たとえば'aabbccdddef'は、d,e,fが奇数個なので、0x111000で表される。これをsのハッシュと呼ぶことにする。すると、以下に置き換えられる。
- 文字列tを並び替えると回文が作れる=tのハッシュで立っているビットの数が0~1個である
(で、ハッシュにするにはしたが、その後がわからず。普通にやるとTLEなので、動的計画法で上手いことやるんだろーなとは思いつつ、時間終了。以降、ほぼ解答の書き写しになってしまうが)
ポイントは、以下の点。
- 最初からi文字目までの文字列s[0,i)のハッシュaiをあらかじめ計算しておく。
- s[l,r)のハッシュ値は、al⊕arで求められる(⊕は、bitwise-xor)
これにより、任意の文字列のハッシュが高速に求められる。その上で、問題を以下の部分問題に分割する。
optiを、s[0,i)の最小の分割数とする。opt0=0として、i=1から順に計算する。最終的にoptNが答えとなる。
いま、optiを求めることを考える。optj (1≤j<i)は計算済みとする。
s[0,i)を、s[k,i)が回文を作れるような位置kでs[0,k)とs[k,i)に分けると、暫定的にopti=optk+1となる。
具体的に考える。s[0,i)='abcdabcab'とする。たとえばk=4として'abcd' + 'abcab'に分けると、'abcab'は回文を作れる。よって、暫定的にopti=(′abcd′の最小分割数)+1となる。'abcd'は1文字ずつに分割するしか無いのでoptk=4。暫定でopti=5となる。
最終的なoptiは、kを1≤k<iの範囲で調べて、その中の最小値となる。
先ほどの例では、k=2として'ab' + 'cdabcab'と分けても、'cdabcab'が回文を作れる。その方がopti=′ab′の最小分割数+1=3となり、より適した分割になる。
が、これではopt1...Nの計算にO(N)、その各計算の中でk=1...iのループにO(N)、合計でO(N2)かかるのでTLE。
次のポイントは、
- 先頭からの2つの文字列のハッシュ値が同じ(ai=aj)場合、opti=optj
2つの文字列のハッシュ値が同じということは、つまりs[i, j)には「各文字がちょうど偶数回ずつ出現する」ということ。回文を作れる文字列の末尾にそのような文字列をくっつけたり(取れるなら)取ったりしても、回文を作れることに変わりない。よって最適な分割数も変わらない。
これで何がいいかというと、kの具体的な値がわからなくても、たとえば「akが0x110010になるようなkのoptk」は一意に決まる。
その上で、
- 調べるのは、s[k,i)が回文を作れるようなkだけでよい
- つまり、s[k,i)のハッシュ値は、{0,20,21,...,225}のいずれかである
- つまり、s[0,k)のハッシュ値akは、ai⊕bのいずれかである。ただしb∈{0,20,21,...,225}
よって、ハッシュ値akをキー、そのハッシュ値を持つ文字列の最小分割数optkを値とした辞書によるDPをおこなう。
調べるのは各aiのループにつき27個で済むため、O(N)となり、間に合う。