ARMERIA

Rubyと競技プログラミングの話 AtCoderやCodeforcesの問題解説記事が多め。

ABC122 D - We Like AGC

D - We Like AGC

解法

問題文の条件を満たすためには、具体的にどのような部分文字列が含まれていてはダメでしょうか。NGパターンを列挙してみましょう。

  • 3文字のNGパターン
    • もちろん AGC はNGです。また、隣り合う2文字を入れ替えて AGC となる GACACG もNGです。3文字のNGパターンはこれで全部です。
  • 4文字のNGパターン
    • X を任意の文字として AXGC はNGパターンです。最初の2文字を入れ替えると AGC ができてしまうからです。同様の理由で AGXC もNGです。4文字のNGパターンはこれで全部です。

列挙してみると意外と少ないことが分かります。また5文字以上のNGパターンはありません。「隣り合う2文字の入れ替えを高々1回行って、連続する3文字の並びを見る」という操作に関係する文字は、必ず連続する4文字以内に収まっているからです。

ということで、連続する4文字の並びを確認すればNGパターンかどうかが判定できることが分かりました。これを言い換えると

文字列の末尾に新しく1つ文字を足した時にNGパターンが新しく発生しないかどうかは、直近の3文字だけを見れば判定できる

ということになります。この考え方を元にDPを構成することが出来ます。

具体的には、

  •  dp\lbrack i \rbrack\lbrack a \rbrack\lbrack b \rbrack\lbrack c \rbrack =  i 文字までの文字を決めていて、最近の3文字が左から  a, b, c となっていて、NGパターンを含んでいない文字列の場合の数

という状態を持ってDPすることができます。 a, b, c はそれぞれ文字 TAGC のどれかだと思ってください(後で実装について補足します)。

 dp\lbrack i \rbrack\lbrack a \rbrack\lbrack b \rbrack\lbrack c \rbrack からの遷移を考えます。この文字列に文字  d を足した時に新しく発生するNGパターンは、先ほど列挙した通り

  • 3文字のNGパターン
    • 文字列  bcd が、AGC GAC ACG のどれか
  • 4文字のNGパターン
    • 文字列  abcd が、X を任意の文字として AXGC AGXC のどちらか

です。これらNGパターンのどれにも当てはまらなければ、 dp\lbrack i+1 \rbrack\lbrack b \rbrack\lbrack c \rbrack\lbrack d \rbrack に遷移することが出来ます。

このDPを全部計算して、最後に  dp\lbrack N \rbrack\lbrack a \rbrack\lbrack b \rbrack\lbrack c \rbrack を全ての  a, b, c について合計したものが答えとなります。

ACコード&実装の補足

Submission #4700029 - AtCoder Beginner Contest 122

実装について少し補足します。まず先ほどの説明では文字をDPテーブルの添字として扱っていましたが、実装においては 0123 といった整数に対応させましょう。私のコードでは添字の 0123 が順に TAGC に対応しています。

次に初期状態について。DPの初期条件を考える時、初期状態(空文字列)には「直近の3文字」がないので少し困ります。今回の場合は「NGパターンに関して意味を持たないような文字」、つまり T が存在すると見なしてしまって良いです。先ほど T は添字 0 に対応させていたので、初期条件を dp[0][0][0][0] = 1 としています。

最後に関数 add(a, b) について。私のコードでは「変数 a に変数 b の値を足してMODを取る」という処理を add(a, b) にやらせています。a = (a+b) % MOD と同等の機能です。