题目:
A. 最短母串
内存限制:512 MiB时间限制:1000 ms标准输入输出
题目类型:传统评测方式:文本比较
题目描述
给定n 个字符串 ,要求找到一个最短的字符串 ,使得这 n个字符串都是它 的子串。
输入格式
第一行是一个正整数 ,表示给定的字符串的个数;
以下的 n+1 行,每行有一个全由大写字母组成的字符串。
输出格式
只有一行,为找到的最短的字符串 。
在保证最短的前提下,如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。
n<=12 1<=|s[i]|<=50
样例
样例输入
2
ABCD
BCDABC
样例输出
ABCDABC
思路:
看到n这么小,首先考虑的就是搜索或者状压,但是搜索的话,就的是所有字符串的全排列,即使加一些减枝,
时间复杂度依旧不可承受。
那么考虑状压,状态设计自然就是dp[i][j],状态为i的串以j结尾的最小母串。
对状态为i进行解释:将i转换成二进制之后每一位上的值,0代表不选,1代表选
状态的转移也很简单,就是枚举接在j之后的子串
同时定义一个g[i][j],表示dp[i][j]的字符串
最后统计一下g[i][j]就行了
对于两个字串,我们只需考虑从哪一位开始重复即可知道重复的部分是多少,
同时考虑到此数组可能多次使用,因此采用离线来降低时间复杂度
总时间复杂度为(n*2^N)
代码
#include<iostream>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
int n;
int minn=INT_MAX;
int overlap[15][15];//j接到i号字符串之后
int dp[5000][15];//状态为i且已j号字符串结尾,1为选择,0为不选择
int top=0;
string g[5000][15];//在状态为i且以j号字符串结尾的结果
string a[15];
string ans;
void solve(int x,int y)
{
for(int i=0;i<a[x].size();i++)
{
bool _f=1;
for(int j=i;j<a[x].size();j++)
{
if(a[y][j-i]!=a[x][j])
{
_f=0;
break;
}
}
if(_f)
{
overlap[x][y]=a[x].size()-i;
return;
}
}
overlap[x][y]=0;
return;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
if(i!=j)
{
solve(i,j);//确定每两个字符串之间的关系
//cout<<overlap[i][j]<<" ";
}
//cout<<endl;
}
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=n;i++)
{
dp[1<<(i-1)][i]=a[i].length();
g[1<<(i-1)][i]=a[i];
}
for(int i=0;i<(1<<n);i++)
{
for(int j=1;j<=n;j++)
{
if(i&(1<<(j-1)))//确保j已经在i这个状态中出现
{
for(int k=1;k<=n;k++)
{
if(!(i&(1<<(k-1))))//确保k没有在i这个状态中出现
{
int to=i|(1<<(k-1));//确定下一个状态
if(overlap[j][k]==-1)//包含关系
{
if((dp[i][j]<dp[to][j])||(dp[i][j]==dp[to][j]&&g[i][j]<g[to][j]))
{
dp[to][j]=dp[i][i];
g[to][j]=g[i][j];
}
}
else
{
string tmp="";
for(int l=overlap[j][k];l<a[k].size();l++)
tmp+=a[k][l];
tmp=g[i][j]+tmp;
if((dp[i][j]+a[k].size()-overlap[j][k]<dp[to][k])||(dp[i][j]+a[k].size()-overlap[j][k]==dp[to][k]&&g[to][k]>tmp))
{
dp[to][k]=dp[i][j]+a[k].size()-overlap[j][k];
g[to][k]=tmp;
}
}
}
}
}
}
}
/*for(int i=0;i<(1<<n);i++)
{
for(int j=1;j<=n;j++)
{
cout<<dp[i][j]<<" ";
}
cout<<endl;
}*/
for(int i=1;i<=n;i++)
{
if(dp[(1<<n)-1][i]<minn)
{
minn=dp[(1<<n)-1][i];
top=1;
ans=g[(1<<n)-1][i];
}
else if(dp[(1<<n)-1][i]==minn)
{
ans=min(ans,g[(1<<n)-1][i]);
}
}
cout<<ans;
return 0;
}