LCS 教学篇

LCS就是最长公共子序列

比如两个串为:

abcicba
abdkscab

ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最长的子序列。

对于LCS的求法,首先可以想到暴力枚举字符串1的每一位,再去匹配字符串2的每一位,这样的复杂度明显很高,不适用长度太大的字符串。

那么我们可以考虑用一个二维矩阵dp[][],dp[i][j]储存str1的前i项和str2前j项的LCS,当str1[i]==str2[j]的时候,假设dp[i-1][j-1]已经求出,那dp[i][j]肯定是dp[i-1][j-1]+1。如果str1[i]!=str2[j],因为我们求的是子序列,要把之前的关系传递下来,所以dp[i][j]=max(dp[i][j-1], dp[i-1][j])。这样就完成LCS了,dp[lenstr1][lenstr2]就是LCS的长度,那么我们又要怎么输出这串LCS呢。我们先看一下dp存的内容。

假设

str1 = abcicba

str2 = abdkscab

dp[][]则为

1 1 1 1 1 1 1 1
1 2 2 2 2 2 2 2
1 2 2 2 2 3 3 3
1 2 2 2 2 3 3 3
1 2 2 2 2 3 3 3
1 2 2 2 2 3 3 4
1 2 2 2 2 3 4 4

其实LCS求的也是最长主对角线,那么我们从x=lenstr1,y=lenstr2开始找对角线。我们考虑,在处理dp时,如果str1[i]==str2[j],dp[i][j]=dp[i-1][j-1]+1,这个时候dp[i-1][j]和dp[i][j-1]一定不等于dp[i][j]的,所以我们要找到dp[x][y]!=dp[x-1][y]&&dp[x][y]!=dp[x][y-1]的地方,输出str1[x]或者str2[y],如果不满足条件,则继续往和dp[x][y]相同的位置找。因为寻找是倒着找的,所以直接输出也会是倒着的,可以考虑用递归写一发。

对于上面的字符串,输出大概是这么遍历的:(数字代表遍历次序)

11 0   0 0 0 0 0 0
1   10 9 8 7 0 0 0
0   0   0 0 0 6 5 0
0   0   0 0 0 0 4 0
0   0   0 0 0 0 3 0
0   0   0 0 0 0 0 2
0   0   0 0 0 0 0 1

51nod有题目,可以做一下:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1006

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <string>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#define X first
#define Y second
#define clr(u,v); memset(u,v,sizeof(u));
#define in() freopen("data","r",stdin);
#define out() freopen("ans","w",stdout);
#define Clear(Q); while (!Q.empty()) Q.pop();
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e3 + 10;
const int INF = 0x3f3f3f3f;
char str1[maxn], str2[maxn];
int dp[maxn][maxn];
void print(int i, int j)//输出函数
{
    if (i == 0 || j == 0) return ;
    if (dp[i-1][j] == dp[i][j]) print(i - 1, j);
    else if (dp[i][j-1] == dp[i][j]) print(i, j - 1);
    else
    {
        print(i - 1, j - 1);
        putchar(str1[i]);
    }
}
int main()
{
    scanf("%s%s", str1 + 1, str2 + 1);
    int len1 = strlen(str1 + 1), len2 = strlen(str2 + 1);
    for (int i = 1; i <= len1; i++)
        for (int j = 1; j <= len2; j++)
        {
            if (str1[i] == str2[j]) dp[i][j] = dp[i-1][j-1] + 1;
            else dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
        }
//    for (int i = 1; i <= len1; i++)
//    {
//        for (int j = 1; j <= len2; j++)
//            printf("%d ", dp[i][j]);
//        puts("");
//    }
    print(len1, len2);
    return 0;
}
原文地址:https://www.cnblogs.com/scaugsh/p/6390376.html