たくさん解かれているのに部分点すらわからないので、書いたことのない文字列アルゴリズムだろうなと検討をつける。Aho-Corasickがいちばんそれっぽいので、貼るととりあえず部分点が取れた。
∣A∣∣B∣個の文字列間で遷移の共通部分が多いことを利用できれば満点が取れそう。そして、そのためにはAho-Corasickをちゃんと理解する必要がありそう。しかし、ei1333さんのコードを見ていても理屈がよくわからないので、明らかな性質だけでなんとかすることにした:
- Aho-Corasickのマッチは部分文字列の一致をひとつずつカウントする。つまり、まとめてカウントしたりはできないので、素朴にやるとマッチする文字列の長さに対してO(N2)になる。とりあえず、トライ木を作ったらノード毎に重みの和をまとめておいたほうがよさそう。たぶんそれでオーダーも落ちる。
- Aho-Corasickの状態は今いるノードだけで決まる。つまり、ノードが同じで以降の文字列が同じならその後の遷移はすべて同じ。
以上に基づくと、次の配列を作っておけばよいはず:
cumulA[i]:=Aの区間[0,i)を走査した後のスコア
nodeA[i]:=Aの区間[0,i)を走査した後のノード
cumulB[i]:=Bの区間[0,i)を走査した後のスコアを0として、そこから最後まで走査して得られるスコア。
nodeB[i]:=Bの区間[0,i)を走査した後のノード
A,Bをそれぞれ[0,i1,),[i2,length(B))で切った時のスコアはcumulA[i1]にnodeA[i1]からB[i2,length(B))を走査して得られるスコアを足したものになるが、この途中で、Bの位置iを走査する際にノードがnodeB[i]と一致していることが確認できたらcumulB[i]を足して打ち切ってよい。単語の長さが高々26なので、i1からそれだけ遷移すれば一致するはず。計算量はたぶんアルファベットの数をαとしてO(α∣A∣∣B∣+∑iSi)。
そしてまだAho-Corasickを理解していない……