Google Code Jam Japan 2011 本戦 C. ワイルドカード
C : ワイルドカード
- 以下のようなDP配列 dp[i][j] を用意する
- i : aにマッチし、最後の"*"を除くとi文字目までマッチするパターン
- j : bにマッチし、最後の"*"を除くとj文字目までマッチするパターン
- j = b.size()+1 のときはbにマッチしないパターンを表す
- 初期化はaの接頭辞 + "*" のパターンで行う
- dp[0][0] = "*"
- if(a[0,i] = b[0,i]) dp[i][i] = a[0,i] + "*";
- else dp[i][b.size()+1] = a[0] + "*";
- DP表の更新は、aのi文字目からの部分文字列を使って行う
- a[i,k] : aのi文字目からk文字目までを取り出した部分文字列
- bのj文字目以降で、a[i,k] が出現する最左位置をpとする
- a[i,k]が出現する場合は、
- dp[k][p+(k-i+1)] = min(dp[k][p+(k-i+1)], dp[i][j] + a[i,k] +"*")
- この場合は新しいパターンの最後に"*"をつけておけばbにマッチする
- a[i,k]が出現しない場合は、a[i,k]を足すとbにマッチしないパターンになるので、
- dp[k][b.size()+1] = min(dp[k][b.size()+1], dp[i][j] + a[i,k] + "*")
- k == a.size()-1 の場合は、特別処理が必要
- dp[i][j] + a[i,k] の後に"*"をつけなくても a全体 にマッチするパターンになる
- このパターンは、a[i,k]がbの接尾辞となっていなければマッチしない
- このとき、dp[k][b.size()+1] = min(dp[k][b.size()+1], dp[i][j] + a[i,k])
- ちなみに、今回の文字列比較の定義では A < B なら AC < BC が成り立つ
- ので、このような動的計画法で最短のものを求めていって良い
- 計算量はO(n^4)だけど、ジャッジデータに対して20秒近くかかった
書いてみたコード
- C++版
#include <iostream> #include <string> using namespace std; string comp(string a, string b){ if(a=="") return b; if(b=="") return a; if(a.size() != b.size()) return a.size() < b.size() ? a : b; int cntA = 0, cntB = 0; for(int i=0;i<a.size();i++) if(a[i]=='*') cntA++; for(int i=0;i<b.size();i++) if(b[i]=='*') cntB++; if(cntA != cntB) return cntA < cntB ? a : b; return a < b ? a : b; } string solveLarge(string a, string b){ int n = a.size(), m = b.size(); string dp[52][52]; for(int i=0;i<=n+1;i++) for(int j=0;j<=m+1;j++) dp[i][j] = ""; dp[0][0] = "*"; for(int i=0;i<n;i++){ if(i>=m || a[i]!=b[i]){ dp[i+1][m+1] = a.substr(0, i+1) + "*"; break; } else { dp[i+1][i+1] = a.substr(0, i+1) + "*"; } } for(int i=0;i<n;i++){ for(int j=0;j<=m+1;j++) dp[i+1][j] = comp(dp[i+1][j], dp[i][j]); for(int j=0;j<=m;j++){ if(dp[i][j] == "") continue; string pat = ""; int l = 0; for(int k=i;k<n;k++){ pat += a[k]; l++; int p = b.find(pat, j); if(p == string::npos){ dp[k+1][m+1] = comp(dp[k+1][m+1], dp[i][j]+pat+"*"); break; } else { dp[k+1][p+l] = comp(dp[k+1][p+l], dp[i][j]+pat+"*"); } } pat = a.substr(i); if(b.find(pat, max(j, m-(int)pat.size())) == string::npos){ dp[n][m+1] = comp(dp[n][m+1], dp[i][j]+pat); } } } return comp(a, dp[n][m+1]); } int main(){ int TEST; cin >> TEST; for(int test=1;test<=TEST;test++){ string a, b; cin >> a >> b; printf("Case #%d: %s\n", test, solveLarge(a, b).c_str()); } }