前言
有空串坑了我老半天,害得我改这改那…
题目
题目大意
输入两个A~Z组成的字符串(长度均不超过30),找一个最短的串,使得输入的两个串均是它的子序列(不要求连续出现)。你除了统计最短串的长度,还要统计长度最短串的个数。
样例给你们细心地Co下来~
in
i
n
3
ABAAXGF
AABXFGA
ABA
BXA
AABBA
BBABAA
out o u t
Case #1: 10 9
Case #2: 4 1
Case #3: 8 10
思路
DP定义
咳咳,这道题我用了一个极为好理解的DP定义,对于最后两个答案都好处理:
f[i][j][l]:a串前i个,b串前j个组成长度为l的串的方案数
f
[
i
]
[
j
]
[
l
]
:
a
串
前
i
个
,
b
串
前
j
个
组
成
长
度
为
l
的
串
的
方
案
数
注意,这里l是
max(i,j)<=l<=i+j
m
a
x
(
i
,
j
)
<=
l
<=
i
+
j
的
状态转移方程式
其实也较好写:
①当
a[i]==b[j]
a
[
i
]
==
b
[
j
]
时
那么相当于组合串的第
l
l
位就是a[i]|b[j],那么直接
②当
a[i]!=b[j]
a
[
i
]
!
=
b
[
j
]
时
那我们就要考虑组合串的第
l
l
位到底放还是放
b[j]
b
[
j
]
了
Ⅰ.放
a[i]
a
[
i
]
注意这是填表法,所以
f[i][j][l]+=f[i−1][j][l−1]
f
[
i
]
[
j
]
[
l
]
+
=
f
[
i
−
1
]
[
j
]
[
l
−
1
]
将
l
l
位留给放
Ⅱ.放
b[j]
b
[
j
]
同理,
f[i][j][l]+=f[i][j−1][l−1]
f
[
i
]
[
j
]
[
l
]
+
=
f
[
i
]
[
j
−
1
]
[
l
−
1
]
将
l
l
位留给放
那么合起来就是
f[i][j][l]=f[i−1][j][l−1]+f[i][j−1][l−1]
f
[
i
]
[
j
]
[
l
]
=
f
[
i
−
1
]
[
j
]
[
l
−
1
]
+
f
[
i
]
[
j
−
1
]
[
l
−
1
]
初始化
我们初始化的时候不仅要把 f[0][0][0] f [ 0 ] [ 0 ] [ 0 ] 赋为1,而且f[i][0][i](1<=i<=n)和f[0][j][j](1<=j<=m)都要为1,这样就可以Dp了
输出答案
我们再看看Dp定义:
f[i][j][l]:a串前i个,b串前j个组成长度为l的串的方案数
f
[
i
]
[
j
]
[
l
]
:
a
串
前
i
个
,
b
串
前
j
个
组
成
长
度
为
l
的
串
的
方
案
数
那么我们找答案的时候就直接枚举
l
l
从找答案,只要
f[n][m][l]
f
[
n
]
[
m
]
[
l
]
有值,就说明有方案,这里题目要求优先满足长度最小,所以直接输出f[n][m][l]和
l
l
<script type="math/tex" id="MathJax-Element-50">l</script>就可以了
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int read(){
int f=1,x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
#define MAXN 30
#define INF 0x3f3f3f3f
char a[MAXN+5],b[MAXN+5];
int f[MAXN+5][MAXN+5][2*MAXN+5];
int main(){//a串前i个,b串前j个组合为长度为l的方案数
int T=read();
for(int t=1;t<=T;t++){
cin.getline(a+1,MAXN+5);cin.getline(b+1,MAXN+5);
int n=strlen(a+1),m=strlen(b+1);
f[0][0][0]=1;//初始化
for(int i=0;i<=n;i++)
f[i][0][i]=1;
for(int j=0;j<=m;j++)
f[0][j][j]=1;
for(int i=1;i<=n;i++)//Dp
for(int j=1;j<=m;j++)
for(int l=max(i,j);l<=i+j;l++){
if(a[i]==b[j]) f[i][j][l]=f[i-1][j-1][l-1];
else f[i][j][l]=f[i-1][j][l-1]+f[i][j-1][l-1];
}//Dp状态转移方程式如上述
for(int i=max(n,m);i<=n+m;++i)
if(f[n][m][i]){//找答案
printf("Case #%d: %d %lld\n",t,i,f[n][m][i]);
break;
}//注意清空,否则会错
memset(f,0,sizeof(f));
}
return 0;
}
题外话
做完后看网上什么与LIS有关,什么答案要用容斥原理输出,都太弱~~~