physics0523's 精進ログ

主に競プロの面白い問題の解法をメモします

周期性補題の利用 : Blocks(CC:UWCOI20F)

Blocks を解くときに使った、新規性のある話題なのでメモ。

長さ \(N\) の文字列 \(S\) があります。

「単位文字列が何度繰り返されているか」を (文字列の長さ) / (文字列の長さの約数である、文字列の周期) の 最大値で定義します。

 

例えば、

"hellohellohello" なら 3 (周期長:5)

"heyhello" なら 1 (周期長:8)

"aabaabaabaab" なら 4 (周期長:3)

"zzzzz" なら 5 (周期長:1) 

となります。

 

文字列 \(S=S_0S_1S_2...S_{N-1}\) に対する以下のクエリを高々 \(Q\) 回使って、「単位文字列が何度繰り返されているか」を求めてください。

? x \(S_0\) と \(S_x\) から1文字づつ進めて比較を行うことを、両者が一致しなくなるか \(x=N\) に達するまで繰り返す。このとき、両者が一致した回数を返す。

 

例えば、

"aabaabaab" に対して

? 1 \(= 1\)

a[a]baabaab 

*[a]abaabaab

 

? 2 \(= 0\)

aabaabaab 

**aabaabaab

 

? 3 \(= 6\)

aab[aabaab] 

***[aabaab]aab

 

となります。

 

\(1 \le N \le 10^6\)

\(Q = 9\)

( (小課題1) \(Q=240\) , (小課題2) \(Q=20\) )

結局、求めるべきものは文字列の長さの約数である最短の周期です。

 

まず、文字列の周期と聞いて思い浮かぶのは  KMPを利用したこれ。  しかし、今回はこれではうまくいきそうにありません。(何せ元の \(S\) に関する情報は得られないので…)

 

ここで、 周期性補題 - Qiita という話題を見つけました。証明はリンク先にぶん投げて、以下の結論(周期性補題)だけを取り出して活用します。

文字列 \(S\) が周期 \(p,q\)を持つとする。

\(p+q \le |S| + gcd(p,q)\) ならば、 \(gcd(p,q)\) も文字列 \(S\) の周期。

 

ここで、今回考えているのは、\(|S|\) の約数である周期 \(p,q\) です。この条件のもとでは、以下が全面的に成立します。

文字列 \(S\) が周期 \(p,q\)を持つならば、 \(gcd(p,q)\) も文字列 \(S\) の周期。

 

(証明)

(i) \(p,q\) の少なくとも一方が \(|S|\) の場合、考えている \(p,q\) が \(|S|\) の約数であることから \(gcd(p,|S|) = p\) または \(gcd(|S|,q) = q\)が成立する。

(ii) \(p,q\) がともに \(|S|\) 未満の場合、 \(p,q\) は \(|S|\) の約数なので、ともに高々 \(\frac{|S|}{2}\) である。 よって、 \(p+q \le |S|\) が成立するので周期性補題より成立。

 

元の問題に戻ります。\(x\) を \(|S|\) の約数とすると、「? x \(= N-x\)」⇔「文字列 \(S\) は周期 \(x\) をもつ」が成立します。

ここまで来れば、あとは具体例を用いて理解するのが早いでしょう。

\(N=720=2^4 \times 3^2 \times 5\) とします。

ここで、解が \(gcd\) を使って求められることを念頭に置くと、素因数の数をひとつづつ当てたくなります。

例えば、解に含まれる \(2\) の素因数を当てたい場合、 \(\frac{720}{2^k}\) がどこまで周期となるかを二分探索してやればよいです。これを全ての素因数について順番に行うと、 \(Q=9\) に収まります。