今晚有点失望,但是也是自己作祟,不能对自己喜欢的girl有太多的要求,至少不能想要求自己一样....因为严于律己宽于待人嘛~~~
题目思路:给出n(n<=10)个字符串,求出这些字符串在某种排列之后形成的(重叠的可以减去)新串最短
思路:这个题目爆搜可以求解,但是最近做了道状态压缩DP,所以坚持用状态压缩DP去做了,但是这个题目描述真的是很坑爹,注意一点就可以了:是不能存在包含在内的情况出现,必须是首尾相连的,考虑到这一点就基本可以了.
比如这组数据:
2
ACCA
CC
输出6才AC.至少我的改为6才AC
dp[i][k]表示的是在i状态下以k字符串结尾的新串最短长度.
注意之所以要考虑到还要枚举k这个状态,因为k这个最后的字符串对下一个匹配是有影响的.
我的字符串预处理只是简单模拟:pre[i][j]表示的是i,j字符串这样的排列顺序,他们的最大公共子串(首尾相连的情况下).
这道题有两个小小的trick:首先是字符串预处理,这里可以为多组数据省下不少时间,另外就是dp的边界数组初始化好很重要,比如这道题的对于每个字符串本身,以自己为尾串的最短长度是本身.
其实这道题:队长ZZY还有思维帝军哥给我指出了一点,其实如果能够有字符串包含的情况的话,就绝不能用我这样的状态方程来解决了.
比如这组数据:
4
G
T
C
GTC.
在这道题的意思是4,但是如果能够包含的话就是3了.
为什么不能用我这个dp来做呢,因为我的状态方程只是与尾字符串有关系,但是如果当前所求的情况与前面的字符串都有关系的话,也就是当前的状态必须要考虑到前面的所有状态,那么这种状态我的状态方程明显没有包含.
AC Program(无注释版):
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<map>
#define inf 10000000
using namespace std;
int vis[1050];
string mm[12];
int pre[12][12];
int length[12];
int dp[1050][12];
int fn(string s1 ,string s2)
{
int len1=s1.length();
int len2=s2.length();
for(int i=len2;i>=1;i--)
{
if((len1-i)<0)continue;
string t1=s1.substr(len1-i);
string t2=s2.substr(0,i);
if(t1.find(t2,0)==0)
{
return i;
}
}
return 0;
}
int main()
{
int test,n,state;
cin>>test;
while(test--)
{
cin>>n;
state=(1<<n)-1;
for(int i=0;i<n;i++)
{
cin>>mm[i];
length[i]=mm[i].length();
}
memset(pre,0,sizeof(pre));
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(i==j)continue;
pre[i][j]=fn(mm[i],mm[j]);
}
}
for(int i=0;i<1050;i++)
for(int j=0;j<11;j++)
dp[i][j]=inf;
for(int i=0;i<n;i++)
{
dp[(1<<i)][i]=length[i];
}
memset(vis,0,sizeof(vis));
for(int i=0;i<state;i++)
{
for(int k=0;k<n;k++)
{
if(i&(1<<k)==0)
continue;
for(int j=0;j<n;j++)
{
if(i&(1<<j))
continue;
int newstate=((1<<j)|i);
int tmp=dp[i][k]+length[j]-pre[k][j];
if(dp[newstate][j]>tmp)
dp[newstate][j]=tmp;
}
}
}
int msum=inf;
for(int i=0;i<n;i++)
{
msum=min(dp[state][i],msum);
}
cout<<msum<<endl;
}
return 0;}
AC Program(注释版):
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<map>
#define inf 10000000
using namespace std;
int vis[1050];
string mm[12];
int pre[12][12];
int length[12];
int dp[1050][12];//求i状态下以k结尾的最短字符串长度.
int fn(string s1 ,string s2)
{
int len1=s1.length();
int len2=s2.length();
for(int i=len2;i>=1;i--) //i表示截取几个数据
{
if((len1-i)<0)continue;//注意了!!
string t1=s1.substr(len1-i);//从len1-i开始,默认剪刀最后
//system("pause");
string t2=s2.substr(0,i);//直接截取来匹配,不能直接在母串中查找,因为有可能存在重复的子串
if(t1.find(t2,0)==0)//string::npos注意:这个位置
{
return i;
}
}
return 0;
}
int main()
{
//freopen("f_1.in","r",stdin);
//freopen("f_1.out","w",stdout);
int test,n,state;
cin>>test;
while(test--)
{
cin>>n;
state=(1<<n)-1;
for(int i=0;i<n;i++)
{
cin>>mm[i];
length[i]=mm[i].length();
}
//预处理.
memset(pre,0,sizeof(pre));
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(i==j)continue;
pre[i][j]=fn(mm[i],mm[j]);
}
}
//初始化
for(int i=0;i<1050;i++)
for(int j=0;j<11;j++)
dp[i][j]=inf;
for(int i=0;i<n;i++)
{
dp[(1<<i)][i]=length[i];
}
memset(vis,0,sizeof(vis));
for(int i=0;i<state;i++)
{
for(int k=0;k<n;k++)
{
//if(dp[i][k]==inf)
// continue;
if(i&(1<<k)==0)//k这个串不在里面就不用枚举以他为结尾的呗
continue;
for(int j=0;j<n;j++)
{
//if(i==j)
// continue;
if(i&(1<<j))//加入编号为j的字符串
continue;
int newstate=((1<<j)|i);
int tmp=dp[i][k]+length[j]-pre[k][j];
if(dp[newstate][j]>tmp)
dp[newstate][j]=tmp;
}
}
}
int msum=inf;
for(int i=0;i<n;i++)
{
//cout<<i<<" "<<dp[state][i]<<endl;
msum=min(dp[state][i],msum);
}
cout<<msum<<endl;
}
//system("pause");
return 0;}