arrows blog

解いた問題などを適当に書いていきます。

AOJ 1320 - City Merger

問題概要

n個の文字列が与えられる。これらを連結させてできる最小の文字列の長さを出力せよ。連結とは、例えば、ABCとBCDを連結するならばABCDとなる。なお、ある文字列が他の文字列の中に完全に含まれていても良い。

制約

  • n ≤ 14

解法

任意の二つの文字列に対して辺を張りbitDPやる。
完全に文字列に含まれるものは取り除く。
dp[頂点の集合][前回の頂点] := 最小の長さ。

コード

#include <bits/stdc++.h>
 
using namespace std;
 
#define MAX 15
#define INF 1e9
 
int N;
int G[MAX][MAX],bit;
int len[MAX];
int dp[1<<MAX][MAX];
string s[MAX];
 
int calc(int a,int b){
  string s1 = s[a],s2 = s[b];
  int l1 = len[a],l2 = len[b];
 
  if(s2.find(s1) != string::npos &&
     s1.find(s2) == string::npos){
    bit |= (1<<a);
    return 0;
  }else if(s1.find(s2) != string::npos &&
           s2.find(s1) == string::npos){
    bit |= (1<<b);
    return 0;
  }
   
  for(int i = min(l1,l2) ; i >= 0 ; i--){
    string subs1 = s1.substr(l1-i,i);
    string subs2 = s2.substr(0,i);
    if(subs1 == subs2){
      return subs1.size();
    }
  }
  return 0;
}
 
int solve(int S,int v){
  if(S == (1<<N)-1) return 0;
  if(dp[S][v] != -1) return dp[S][v];
  int res = INF;
  for(int i = 0 ; i < N ; i++){
    if((S >> i) & 1) continue;
    res = min(res,solve(S|(1<<i),i)+len[i]-G[v][i]);
  }
  return dp[S][v] = res;
}
 
int main(){
  while(cin >> N,N){
    bit = 0;
    memset(dp,-1,sizeof(dp));
    fill(G[0],G[0]+MAX*MAX,INF);
    for(int i = 0 ; i < N ; i++){
      cin >> s[i];
      len[i] = s[i].size();
    }
    for(int i = 0 ; i < N ; i++){
      for(int j = 0 ; j < N ; j++){
        if(i == j) continue;
        G[i][j] = calc(i,j);
      }
    }
    int res = INF;
    for(int i = 0 ; i < N ; i++){
      if((bit >> i) & 1) continue;
      res = min(res,solve(bit|(1<<i),i)+len[i]);
    }
    cout << res << endl;
  }
  return 0;
}