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: