题意:给定一个字符串,还有它的四个字串, 选择合适的位置让这些子串覆盖原串,问最多和最少的覆盖字符数。
做法:先用个法子求出每个字符位置是否可以放某个串,然后建立状态,dp[i][j],i是当前探索的位置,j是匹配点。值得注意的是,每个点可能可以匹配多个字串,这个可以用类似背包的手法解决,即在一个位置多次匹配计算修正,具体看代码,其实数组是可以降成一维的
#include <cstdio>
#include <cstring>
#define max(a,b) ((a)>(b) ? (a):(b))
#define min(a,b) ((a)<(b) ? (a):(b))
#define eps 1e5
const int sLEN=64;
const int LMT=5000;
const int lim=15;
const int sub=4;
int dpx[sLEN+10][lim+10],dpn[sLEN+10][lim+10];
int can[LMT][sub];
void init(void)
{
int i,j,k;
memset(can,0,sizeof(can));
for(j=0;j<70;j++)
for(k=0;k<70;k++)
{
dpx[j][k]=-eps;
dpn[j][k]=eps;
}
dpx[0][0]=dpn[0][0]=0;
}
int main(void)
{
char sec[LMT],ord[70];
int i,pos,ii,jj,j,st,t,anx,ann;
sec[0]=9;
while(~scanf("%s",&sec[1]))
{
init();
for(i=0;i<sub;++i)
{
scanf("%s",ord);
for(pos=1;sec[pos];++pos)
{
for(ii=pos,jj=0;sec[pos]&&ord[jj]&&sec[ii]==ord[jj];++ii,++jj);
if(!ord[jj])can[pos][i]=jj;
}
}
anx=-eps;ann=eps;
for(i=1;sec[i];++i)
{
for(j=0;j<=sLEN;++j)
for(st=0;st<=lim;++st)//0代表匹配位置在当前位置的后边,当前位置增加1,匹配位置减少1,但0要另算
{
if(j)
{
dpn[j][st]=dpn[j+1][st];
dpx[j][st]=dpx[j+1][st];
}
else
{
dpn[j][st]=min(dpn[j][st],dpn[j+1][st]);
dpx[j][st]=max(dpx[j][st],dpx[j+1][st]);
}
}
for(t=0;t<sub;++t)//当前位置匹配一下。
if(can[i][t])
for(jj=0;jj<=sLEN;++jj)
for(st=0;st<=lim;++st)
if(!(st&(1<<t)))
{
j=max(jj,can[i][t]);
dpn[j][st|(1<<t)]=min(dpn[j][st|(1<<t)],dpn[jj][st]+max(0,j-jj));
dpx[j][st|(1<<t)]=max(dpx[j][st|(1<<t)],dpx[jj][st]+max(0,j-jj));
}
anx=max(anx,max(dpx[1][lim],dpx[0][lim]));
ann=min(ann,min(dpn[1][lim],dpn[0][lim]));
}
printf("%d %d\n",ann,anx);
}
return 0;
}