タイトル
環は、N(N <= 16)文字列指定された文字列は、元のループの最小の長さで見つける、環上にあります
分析
この問題は非常に煩雑な感じで、鍵が所定の配列の文字列であるが、時計回り、反時計回りであってもよく、またはあってもよく、また、開始位置を決定し、最後にあっても環(ある、端部に接続された端部)にし、どのようにリングを確保するためには最も小さい
BGGBを与えた最初のIの誤解でとの戦いが読み、BGG BGG代わりBGGBGGから出ているコードをすべての文字列をしているサブストリングリングを決定した後、何のサイクルがありません可能
前処理まず、他の文字列の文字列の部分文字列を削除され、多くのヒントがありますが、私は非常にシンプルな感じ、あなたは劉Rujiaコードを参照することができます。
それはリングであるためと、[0] [0]出発DPとして0正シーケンス列[1] [0] [ 0] = LEN [0]、 更新ステータスブラシテーブル方式
列の検索終了が決定されます結果
まず、一緒に列ラインに
状態DP [S] [j] [ k]はsは、jが文字列連結の最後で、文字列の代表的なセットのコレクションが接続されたあるK = 0又は1であり、jが表します配列、DP [S] [J] [k]は、文字列sの重複の長さを表し
、最後にJ(比較決定するJ必要性)を0に接続されているが、カバー部を減算し、環を形成することができる
状態遷移式:のため文字列jの端部に接続された状態の既存の、順序kが、私は彼の右の文字列に追加していくことができ、私はL、以下の伝達方程式の次数であります
最後に、要求の対象ため、結果は、出力される2、1である場合
概要
Orのようなこの問題に精通していない圧力DP圧力DPを使用することが明らかであるように、問題は、それは注目に値する(追加のディメンションの順序は、文字列の最後を表し)、計算を簡単にするためにいくつかのトリックでこの質問を使用する順序を表す文字列どのようになっています
//
// Created by Zhao Pengfei on 2019/11/14.
//
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <utility>
using namespace std;
const int maxn=16;
struct Item{
string str,rev;
bool operator < (const Item &rhs) const {
return str.length()<rhs.str.length();
}
};
//overlap[i][j][k][l]中,i,j代表两个字符串的序号,k代表i的顺序,l代表j的顺序
//dp[s][j][k]
int n,m,overlap[maxn][maxn][2][2],dp[1<<16][maxn][2],len[maxn];
Item items[maxn];
string s[maxn][2]; //小技巧,这样分成二维表示顺序,方便传入Overlap函数中计算,不用写if判断顺序
//a(left),b(right) overlap length
int Overlap(string &a,string &b){
int len1=a.length();
int len2=b.length();
for(int i=0;i<len1;++i){
if(i+len2<=len1) continue; //没有到能够重合的位置
bool flag=true;
for(int j=0;i+j<len1;++j){
if(a[i+j]!=b[j]){
flag=false;
break;
}
}
if(flag){
return len1-i;
}
}
return 0;
}
void Init(){
//输入,预处理,删除被包含的子字符串
for(int i=0;i<n;++i){
cin>>items[i].str;
items[i].rev=items[i].str;
reverse(items[i].rev.begin(),items[i].rev.end());
}
sort(items,items+n);
m=0;
for(int i=0;i<n;++i){
bool flag=true;
for(int j=i+1;j<n;++j){
if(items[j].str.find(items[i].str)!=string::npos
||items[j].str.find(items[i].rev)!=string::npos ) {
flag = false;
break;
}
}
if(flag){
s[m][0]=items[i].str;
s[m][1]=items[i].rev;
len[m]=s[m][0].length();
m++;
}
}
for(int i=0;i<m;++i){
for(int j=0;j<m;++j){
for(int k=0;k<2;++k){
for(int l=0;l<2;++l){
overlap[i][j][k][l]=Overlap(s[i][k],s[j][l]);
}
}
}
}
}
void update(int &x,int y){
if(x==-1 || y<x) x=y;
}
//这里使用刷表法进行状态转移
void Solve(){
memset(dp,-1,(1<<m)*sizeof(dp[0]));
dp[1][0][0]=len[0];
int S=(1<<m);
//因为是刷表法,s可以小于S-1
for(int s=0;s<S-1;++s){
for(int j=0;j<m;++j){
for(int k=0;k<2;++k){
if(dp[s][j][k]<0) continue;
int t=dp[s][j][k];
for(int i=0;i<m;++i){ //选择下一个字符串
if(s&(1<<i)) continue;
update(dp[s|(1<<i)][i][0],t+len[i]-overlap[j][i][k][0]);
update(dp[s|(1<<i)][i][1],t+len[i]-overlap[j][i][k][1]);
}
}
}
}
int ans=-1;
for(int i=0;i<m;++i){
for(int k=0;k<2;++k){
if(dp[S-1][i][k]==-1) continue;
update(ans,dp[S-1][i][k]-overlap[i][0][k][0]);
}
}
if(ans<=2) printf("2\n");
else printf("%d\n",ans);
}
int main(void){
while(cin>>n && n){
Init();
Solve();
}
}