题意:
求两篇文章中单词的最长公共子序列。
分析:
我们令 dp[ i ][ j ] 表示第一篇文章判断到第 i 个单词,第二篇文章判断到第 j 个单词时的最长公共子序列的长度。
状态转移:
dp[ i ][ j ] = dp[ i - 1 ][ j - 1 ] + 1;(第 i 个单词和第 j 个相同);
或者 dp[ i ][ j ] = max( dp[ i - 1 ][ j ], dp[ i ][ j - 1] );(第 i 个单词和第 j 个不相同)。
用road[ i ][ j ] 记录路径,见代码。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int dp[105][105], road[105][105];
char ch1[105][35], ch2[105][35];
int ok;
void print(int x, int y) //递归打印结果
{
if(x < 1 || y < 1) return; //函数出口
if(road[x][y] == 0) //表示要输出最长公共子序列中的单词
{
print(x - 1, y - 1); //先继续递归,因为要正序输出
if(ok) ok = 0; //控制空格
else printf(" ");
printf("%s", ch1[x]); //输出答案
}
else if(road[x][y] == 1)
{
print(x - 1, y);
}
else print(x, y - 1);
}
int main()
{
int s1 = 0, s2 = 1;
while(~scanf("%s", ch1[++s1]))
{
if(ch1[s1][0] == '#')
{
while(1)
{
scanf("%s", ch2[++s2]);
if(ch2[s2][0] == '#') break;
}
for(int i = 1; i < s1; i++)
{
for(int j = 1; j < s2; j++)
if(strcmp(ch1[i], ch2[j]) == 0)
{
dp[i][j] = dp[i - 1][j - 1] + 1; //状态转移
road[i][j] = 0; //两个单词相同,从第i-1行,j-1列转移
}
else
{
if(dp[i - 1][j] > dp[i][j - 1])
{
dp[i][j] = dp[i - 1][j];
road[i][j] = 1; //表示从第i-1行,j列转移而来
}
else
{
dp[i][j] = dp[i][j - 1];
road[i][j] = 2; //表示从第i行,j-1列转移而来
}
}
}
ok = 1;
print(s1 - 1, s2 - 1);
printf("\n");
s1 = 0, s2 = 0;
memset(dp, 0, sizeof(dp));
}
}
return 0;
}
其实还可以不记录路径,直接根据保存结果的dp数组来输出答案
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 100 + 10;
string s1[MAXN], s2[MAXN];
int dp[MAXN][MAXN];
int hasPrin;
void Print(int x, int y) //递归打印答案
{
if (x < 1 || y < 1) return; //出口
if (s1[x] == s2[y]) //答案中有此单词
{
Print(x - 1, y - 1); //先递归打印前面的单词
if (hasPrin == 1) printf(" ");
hasPrin = 1;
cout << s1[x]; //再输出这个单词
}
else if (dp[x - 1][y] > dp[x][y - 1]) Print(x - 1, y); //判断从哪转移而来,递归打印
else Print(x, y - 1);
}
int main()
{
int len1 = 1, len2 = 1;
string s;
while (cin >> s)
{
if (s == "#")
{
while (cin >> s)
{
if (s == "#") break;
s2[len2++] = s;
}
memset(dp , 0, sizeof(dp));
for (int i = 1; i < len1; i++)
for (int j = 1; j < len2; j++) //状态转移方程
{
if (s1[i] == s2[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
hasPrin = 0;
Print(len1 - 1, len2 - 1); //打印答案
printf("\n");
len1 = len2 = 1;
continue;
}
s1[len1++] = s;
}
return 0;
}