AOJ1320 City Merger
問題ページ
City Merger | Aizu Online Judge
概要
要素数がNの文字列集合Sが与えられる。連続した部分文字列にSの要素を全て含むような文字列のうち最短のものを求めろ。 N<=14、(文字列の長さ)<=20
考えたこと
- suffixとprefixが一致しているような文字列の組があれば組み合わせたくなる
- s[i]のsuffixとs[j]のprefixが一致していれば -その長さ をコストにするi->jの有向辺を張りたくなる
- 巡回セールスマンっぽい
- dp[T][i] = (すでに使った文字列の集合Tと最後に使った文字列iをつくる最小の文字列の長さ) をもってbitDPすればできそう
- bitDPを書いてサンプルを試すと通らないのがある
- s[i]にs[j]が完全に内包されているようなとき、s[i]を使えば必ず現れるのでs[j]はわざわざ後ろにつける必要がない
- なので別の文字列に完全に含まれているような文字列は消して考えていい
- 実装すると通った
s.size()-1でオーバーフローさせるやつをやって1WA出したので反省
ソースコード
#include <bits/stdc++.h> using namespace std; using ll = long long; #define int ll using VI = vector<int>; using VVI = vector<VI>; #define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i) #define REP(i, n) FOR(i, 0, n) #define PB push_back const int INF = (1LL<<30); template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); } signed main(void) { cin.tie(0); ios::sync_with_stdio(false); while(true) { int n; cin >> n; if(!n) break; vector<string> t(n); REP(i, n) cin >> t[i]; // 内包されているようなのは消す vector<bool> del(n, false); REP(i, n) REP(j, n) { if(i==j || del[i] || del[j] || t[i].size() < t[j].size()) continue; REP(k, t[i].size()-t[j].size()+1) { string tmp = t[i].substr(k, t[j].size()); // cout << tmp << endl; if(tmp == t[j]) del[j] = true; } } vector<string> s; REP(i, n) if(!del[i]) s.PB(t[i]); n = s.size(); // i->j とつなげるときのsuffixとprefixが一致する最大の長さ VVI trans(n, VI(n, 0)); REP(i, n) REP(j, n) { if(i==j) continue; REP(k, min(s[i].size(), s[j].size())) { string tmp1 = s[i].substr(s[i].size()-k-1), tmp2 = s[j].substr(0, k+1); // cout << tmp1 << " " << tmp2 << endl; if(tmp1 == tmp2) trans[i][j] = k+1; } } // bitDPする VVI dp(1LL<<n, VI(n, INF)); REP(i, n) dp[1<<i][i] = s[i].size(); REP(i, 1LL<<n) REP(j, n) { if(dp[i][j] == INF) continue; REP(k, n) { if(i&1<<k) continue; chmin(dp[i|1<<k][k], dp[i][j]+((int)s[k].size()-trans[j][k])); } } int ans = INF; REP(i, n) chmin(ans, dp[(1LL<<n)-1][i]); cout << ans << endl; } return 0; }