公式

E - 部分列のカウント / Counting Subsequences 解説 by kyopro_friends


この問題は DP で解くことができます。

\(\mathrm{DP}[i][j]\) を「 \((A_1,\dots,A_i)\) の部分列で \((P_1,\ldots, P_j)\) と一致するものの個数」とします。求める答えは \(\mathrm{DP}[N][K]\) です。

このとき、 \(\mathrm{DP}[i][j]\)\(A_i=P_j\) のときに限り \(A_i\) を選ぶかどうか選択でき、次のように定まります。

\(\mathrm{DP}[i][j]=\begin{cases} \mathrm{DP}[i-1][j] + \mathrm{DP}[i-1][j-1] &A_i=P_j \text{ のとき}\\ \mathrm{DP}[i-1][j] & A_i\neq P_j \text{ のとき} \end{cases}\)

この DP で \(\mathrm{DP}[N][K]\) を求めるには状態数 \(O(NK)\) 、各状態の計算が \(O(1)\) であることから \(O(NK)\) で計算できます。

実装例 (C++)

#include<bits/stdc++.h>
#include<atcoder/modint>
using namespace std;
using mint = atcoder::modint1000000007;

int main(){
  int n, k;
  cin >> n >> k;
  vector<int>a(n);
  for(int i=0; i<n; i++) cin >> a[i];
  vector<int>p(k);
  for(int i=0; i<k; i++) cin >> p[i];

  vector<vector<mint>>dp(n+1, vector<mint>(k+1));
  for(int i=0; i<n+1; i++){
    dp[i][0] = 1;
  }

  for(int i=0; i<n; i++){
    for(int j=0; j<k; j++){
      if(a[i] == p[j]){
        dp[i+1][j+1] = dp[i][j+1] + dp[i][j];
      }else{
        dp[i+1][j+1] = dp[i][j+1];
      }
    }
  }
  cout << dp[n][k].val() << endl;
}

実装例 (Python)

MOD = 10**9+7
N, K = map(int, input().split())
A = list(map(int, input().split()))
P = list(map(int, input().split()))

dp = [[0]*(K+1) for _ in range(N+1)]
for i in range(N+1):
  dp[i][0] = 1

for i in range(N):
  for j in range(K):
    if A[i] == P[j]:
      dp[i+1][j+1] = (dp[i][j+1] + dp[i][j]) % MOD
    else:
      dp[i+1][j+1] = dp[i][j+1]

print(dp[N][K])

投稿日時:
最終更新: