题意:给定两个字符串A,B,要求求一个字符串S,使得A,B是S 的子串,求S 的最小长度,以及在此长度下有多少种构成S 的方案。
LCS的变形,S的最小长度肯定是A+B的长度-A和B的最长公共组序列的长度。之后的方案用DP思想去写就可以了。QAQ 我也是看了题解才会写的。
先用lcs 去求A和B 的最长公共子序列。
然后dp[i][j][k]表示构造了i长度的字符串,在利用了A串的前j个字符和B串的前K个字符,此状态下的解决方案总数,那么后面的方程就和LCS 一样了
如果A[j]==B[k],那么dp[i+1][j+1][k+1] += dp[i][j][k]; 不然就转移到右边和下边的状态,dp[i+1][j][k+1] += dp[i][j][k]; dp[i+1][j+1][k] += dp[i][j][k]; 原理类似于LCS。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#pragma warning (disable :4996)
using namespace std;
const int Max = 33;
int lcs[Max][Max];
long long dp[Max << 1][Max][Max];
char str1[Max], str2[Max];
int LCS(int lenstr1, int lenstr2)
{
memset(lcs, 0, sizeof(lcs));
for (int i = 1; i <= lenstr1; i++)
{
for (int j = 1; j <= lenstr2; j++)
{
if (str1[i-1] == str2[j-1])
lcs[i][j] = lcs[i - 1][j - 1] + 1;
else
lcs[i][j] = max(lcs[i - 1][j], lcs[i][j - 1]);
}
}
return lcs[lenstr1][lenstr2];
}
int main()
{
int T;
scanf("%d ", &T);
for (int t = 1; t <= T; t++)
{
memset(dp, 0, sizeof(dp));
dp[0][0][0] = 1;
gets(str1);
gets(str2);
int n, m, lenlcs;
n = strlen(str1);
m = strlen(str2);
lenlcs = LCS(n, m);
int ans = n + m - lenlcs;
for (int i = 0; i <= ans; i++)
for (int j = 0; j <= n; j++)
for (int k = 0; k <= m;k++)
if (dp[i][j][k])
if (str1[j] != str2[k])
{
dp[i + 1][j][k + 1] += dp[i][j][k];
dp[i + 1][j + 1][k] += dp[i][j][k];
}
else dp[i + 1][j + 1][k + 1] += dp[i][j][k];
printf("Case %d: %d %lld\n", t, ans, dp[ans][n][m]);
}
return 0;
}