考え方
DPで前から決めていけば良いことはわかる.DPを更新するためには,
- 前から何番目まで決めたか
- すでに使った文字の集合
- 最後に選んだ文字の種類
dp[i][j][k]
で表す).使った文字の集合はbitで管理できる.【更新ルール】
$i$番目まで決めたとき,$i + 1$番目まで考えると「$i + 1$番目を選ばない」方法がdp[i][j][k]
通り存在する.
さらに,
- $i + 1$番目の文字が未使用であるか
- $i + 1$番目の文字が前ステップの最後の文字と一致しているか
dp[i][j][k]
通り存在する.回答例
各$i + 1$に対し,$i + 1$回目で初めて参加した場合を1として(ループの中で)初期化する.N = int(input()) S = list(input()) S = [ord(s) - ord('A') for s in S] mod = 998244353 dp = [[[0] * 10 for _ in range(1 << 10)] for _ in range(N + 1)] for i in range(N): dp[i + 1][1 << S[i]][S[i]] = 1 for j in range(1 << 10): for k in range(10): dp[i + 1][j][k] += dp[i][j][k] dp[i + 1][j][k] %= mod if (j >> S[i] & 1) and k != S[i]: continue dp[i + 1][j | 1 << S[i]][S[i]] += dp[i][j][k] dp[i + 1][j | 1 << S[i]][S[i]] %= mod ans = 0 for j in range(1, 1 << 10): for k in range(10): ans += dp[N][j][k] ans %= mod print(ans)
【初期化方法を工夫する】
- 答えは,一つも選ばない場合を除く必要があるので,空集合($j=0$)は初期化のために好きに使ってよい.
- 空集合($j=0$)からの遷移が全て$1$になってほしい
dp[0][0][k] = 1
とすれば良い.
N = int(input()) S = list(input()) S = [ord(s) - ord('A') for s in S] mod = 998244353 dp = [[[0] * 10 for _ in range(1 << 10)] for _ in range(N + 1)] dp[0][0][0] = 1 for i in range(N): for j in range(1 << 10): for k in range(10): dp[i + 1][j][k] += dp[i][j][k] dp[i + 1][j][k] %= mod if (j >> S[i] & 1) and k != S[i]: continue dp[i + 1][j | 1 << S[i]][S[i]] += dp[i][j][k] dp[i + 1][j | 1 << S[i]][S[i]] %= mod ans = 0 for j in range(1, 1 << 10): for k in range(10): ans += dp[N][j][k] ans %= mod print(ans)