Official

D - String Bags Editorial by physics0523


結論から言うと、この問題は動的計画法 (DP) により正解できます。

\(dp[\) 袋を何個目まで処理したか \(][\) \(T\) の接頭辞のうちどこまで一致させられたか \(] = \) { 必要な最小金額 } を実現しましょう。

\(i+1\) 袋目に入っている文字列 \(X\)\(S\) の末尾に連結することを考えます。この時、 \(dp[i][|S|]\) から \(dp[i+1][|S|+|X|]\) に遷移するためには、 \(T\)\(|S|+1\) 文字目から \(|S|+|X|\) 文字目を取り出した文字列が \(X\) と一致する必要があります。

今回の制約下では、この判定を愚直に行っても構いません。

また、何もしないということは \(dp[i][j]\) から \(dp[i+1][j]\) への遷移に対応します。

以上の DP を実装すると、この問題に \(O(NM|S||T|)\) といった時間計算量で正解できます。 ( \(M\) … 袋に入った文字列の個数 )

実装例(C++):

#include<bits/stdc++.h>

using namespace std;

int dp[105][105];

int main(){
  for(int i=0;i<105;i++){
    for(int j=0;j<105;j++){
      dp[i][j]=1e9;
    }
  }
  dp[0][0]=0;

  string t;
  cin >> t;
  int tl=t.size();
  int n;
  cin >> n;
  for(int i=0;i<n;i++){
    for(int j=0;j<105;j++){dp[i+1][j]=dp[i][j];}
    int m;
    cin >> m;
    while(m>0){
      m--;
      string s;
      cin >> s;
      int sl=s.size();
      for(int j=0;j+sl<=tl;j++){
        bool ok=true;
        for(int k=0;k<sl;k++){
          if(t[j+k]!=s[k]){ok=false;break;}
        }

        if(ok){
          dp[i+1][j+sl]=min(dp[i+1][j+sl],dp[i][j]+1);
        }
      }
    }
  }

  if(dp[n][tl]>5e8){dp[n][tl]=-1;}
  cout << dp[n][tl] << "\n";
  return 0;
}

posted:
last update: