Kimariji Key Keeper

2 secs 1024 MB
yuki4869's icon yuki4869

解法1:set と隣接する文字列の LCP

文字列 s,ts,t の最長共通接頭辞の長さを LCP(s,t)\operatorname{LCP}(s,t) とします。

文字列 ss の決まり字の長さは、ほかの文字列との LCP の最大値に 11 を加えたものです。つまり、

As=1+maxtT, tsLCP(s,t)A_s=1+\max_{t\in T,\ t\neq s}\operatorname{LCP}(s,t)

となります。

各文字列の最後には 0 が付いているため、ある文字列が別の文字列の接頭辞になることはありません。したがって、この値が s|s| を超えることはありません。

ただし、この方針では文字列長の総和を M=i=1QSiM=\sum_{i=1}^{Q}|S_i| とすると、全体の計算量は O(M2)O(M^2) となり、間に合いません。計算量の削減を考える必要があります。

実は、文字列を辞書順に並べたとき、文字列 ss との LCP が最大になる文字列は、ss の直前または直後に存在します。

実際、ss と長さ kk の接頭辞が一致する文字列は、辞書順で連続して並びます。そのような文字列が存在するなら、ss の直前または直後の少なくとも一方も同じ接頭辞をもちます。

したがって、ss の直前の文字列を ll、直後の文字列を rr とすると、存在しない文字列との LCP を 00 として、

As=1+max(LCP(l,s),LCP(s,r))A_s=1+\max(\operatorname{LCP}(l,s),\operatorname{LCP}(s,r))

と求められます。

現在の文字列を set で辞書順に管理します。

新しい文字列 ss を追加し、その直前を ll、直後を rr とします。追加前には llrr が隣接していましたが、追加後には l,s,rl,s,r の順に並びます。

このとき、隣接関係が変化するのは l,s,rl,s,r だけです。そのため、決まり字の長さが変化する可能性がある文字列もこの 33 つだけです。

よって、追加前の l,rl,r の決まり字の長さを総和から引き、ss を追加した後の l,s,rl,s,r の決まり字の長さを加えれば、答えを更新できます。

隣接する文字列間の LCP も管理しておけば、新しく計算する必要があるのは LCP(l,s)\operatorname{LCP}(l,s)LCP(s,r)\operatorname{LCP}(s,r) だけです。

set への挿入には O(logQ)O(\log Q) 回の文字列比較が必要であるため、文字列長の総和を M=i=1QSiM=\sum_{i=1}^{Q}|S_i| とすると、全体の計算量は O(MlogQ)O(M\log Q) です。


解法2:Trie木

文字列を Trie 木に追加していくことを考えます。

Trie 木の各頂点 vv について、cntvcnt_v を「頂点 vv が表す文字列を接頭辞にもつ、現在の集合 TT 内の文字列数」とします。

文字列 ss の長さ kk の接頭辞に対応する頂点の cntcnt11 なら、その接頭辞によって ss を一意に特定できます。

ss の経路上では、根から進むにつれて cntcnt は増加しないため、決まり字より短い接頭辞の cntcnt はすべて 22 以上です。

したがって、文字列 ss の決まり字の長さは、

As=1+{v  |  v は s の接頭辞であり、cntv2}A_s = 1+ \left| \left\{ v \;\middle|\; v\text{ は }s\text{ の接頭辞であり、}cnt_v\geq 2 \right\} \right|

と表せます。

これをすべての文字列について足し合わせると、求める総和は

T+vrootcntv2cntv|T|+\sum_{\substack{v\neq \mathrm{root}\\ cnt_v\geq 2}} cnt_v

となります。

cntv2cnt_v\geq2 の頂点 vv は、その頂点を通る cntvcnt_v 個の文字列それぞれの決まり字の長さに 11 ずつ寄与するためです。

新しい文字列を追加すると、その文字列に対応する経路上の各頂点で cntvcnt_v11 増えます。

頂点 vv の総和への寄与は、cntv1cnt_v\leq1 なら 00cntv2cnt_v\geq2 なら cntvcnt_v です。そのため、更新前の cntvcnt_v によって答えの変化量は次のようになります。

  • cntv=0cnt_v=0 のとき、00 から 11 になるため変化しない。
  • cntv=1cnt_v=1 のとき、寄与が 00 から 22 になるため、答えに 22 を加える。
  • cntv2cnt_v\geq2 のとき、寄与が cntvcnt_v から cntv+1cnt_v+1 になるため、答えに 11 を加える。

また、文字列数 T|T|11 増えるため、各クエリの最初に答えを 11 増やします。

その後、新しい文字列の各文字を Trie 木に追加しながら、経路上の各頂点について上記の更新を行えばよいです。

各文字列の各文字について Trie 木を一度進むため、時間計算量と空間計算量はともに、文字列長の総和を M=i=1QSiM=\sum_{i=1}^{Q}|S_i| とすると O(M)O(M) です。