题意:给定n个(1<=n<=8)由AGCT组成,长度不大于5的模式串,要求构造一个尽量短的字符串,满足任意一个模式串都是其子序列。(子序列是不一定连续的,连续的指子串)
思路:用IDA*算法 , 枚举需要构造的字符串的长度,然后用dfs+剪枝技巧判断这个长度是否满足。 这里剪枝的评估函数来自于其他字符串剩余所需的最大字符个数。
#include<iostream>
#include<cstdio>
#include<set>
#include<vector>
#include<map>
#include<algorithm>
#include<string.h>
#include<string>
#include<queue>
using namespace std;
#define ll long long
#define ull unsigned long long
const int maxn = 8e2+5;
const ll inf = 34359738370;
const int INF = 1e9+7;
const int has = 99959;
//给定n个长度不超过5的字符串(ACGT组成),要求构造一个最短的字符串使得n个字符串是他的子序列
//IDA*算法 ,枚举搜索树的深度, 验证这个深度能否找出满足条件的解 验证过程就是dfs+剪枝,可以用来解决最短路问题
int n;
char sub[10][10];
int len[10];//每个的长度
int pos[10];//每个目前匹配成功的进度
char book[10]="AGCT";
bool dfs(int steps)//剩余的可以往下搜的层数
{
int need=0;//评估函数 need表示当前情况 完成匹配所需的最乐观的步数
for(int i=1;i<=n;i++) need=max(need,len[i]-pos[i]);
if(!need) return 1;//0 匹配成功
if(need > steps) return 0;//剪枝
bool change[10]={0};//每个i 是否成功匹配到第j个字符串的下一位
for(int i=0;i<4;i++)
{
bool flag=0;//用于剪枝 如果一个也没匹配成功 则无需继续向下
for(int j=1;j<=n;j++)
{
if(pos[j]==len[j]) continue;
if(sub[j][pos[j]+1] == book[i])
{
flag=1;
pos[j]++;
change[j]=1;
}
}
if(flag)
if(dfs(steps-1)) return 1;
for(int j=1;j<=n;j++)
{
if(change[j])
{
change[j]=0;
pos[j]--;
}
}
}
return 0;
}
int main()
{
int T;cin>>T;
while(T--)
{
int l=-1,r=0,ans;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",sub[i]+1);
len[i]=strlen(sub[i]+1);
l=max(l,len[i]);
r+=len[i];
}
while(l<=r)//二分搜索的最大深度
{
memset(pos,0,sizeof(pos));
int mid=(l+r)>>1;
if(dfs(mid))
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
cout<<ans<<'\n';
}
}