清北学堂模拟赛d5t5 exLCS

分析:比较巧妙的一道题.经典的LCS算法复杂度是O(nm)的,理论上没有比这个复杂度更低的算法,除非题目有一些限制.这道题中两个字符串的长度不一样,f[i][j]如果表示第一个串前i个,第二个串前j个的最长公共子序列的话,复杂度会爆.但是LCS的长度很小,能不能换一种状态的表示方法呢?

      回想0/1背包问题的变形,如果体积特别大,价值特别小,我们可以把价值定义在状态里面,设f[i][j]表示前i个物品中价值为j的最小体积,最后扫一遍j看看符不符合条件就可以了,对于这道题我们可以也可以沿用这样的思路,把LCS的长度放到状态里面,设f[i][j]表示str1的前i个,LCS的长度为j的已经匹配到的str2的最左下标,先预处理一下当前位置的字母的下一次出现的位置,然后就可以递推了.

      当dp题中状态很大,答案很小的时候可以把答案定义到状态中,最后扫一下看看是否满足要求就可以了.这种方法有点像二分一样:我知道了答案,检验答案是否可行.

代码中求nextt数组的做法值得学习!

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 1010, inf = 0x7ffffff;
int f[maxn][maxn], nextt[100010][30], n, m, ans;
char a[1010], b[100010];

int main()
{
    scanf("%s%s", a, b);
    n = strlen(a);
    m = strlen(b);
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            f[i][j] = inf;
    for (int i = 0; i < 26; i++)
        nextt[m][i] = inf;
    for (int i = m - 1; i >= 0; i--)
    {
        memcpy(nextt[i], nextt[i + 1], sizeof(nextt[i]));
        nextt[i][b[i] - 'a'] = i;
    }
    f[0][1] = nextt[0][a[0] - 'a'];
    for (int i = 0; i <= n; i++)
        f[i][0] = -1;
    for (int i = 0; i < n - 1; i++)
        for (int j = 0; j <= n && f[i][j] < inf; j++)
        {
            f[i + 1][j] = min(f[i + 1][j], f[i][j]);
            if (j < n)
                f[i + 1][j + 1] = min(f[i + 1][j + 1], nextt[f[i][j] + 1][a[i + 1] - 'a']);
        }
    for (int i = n; i >= 1; i--)
        if (f[n - 1][i] != inf)
        {
            ans = i;
            break;
        }
    printf("%d\n", ans);

    return 0;
}

 

posted @ 2017-10-11 16:24  zbtrs  阅读(309)  评论(0编辑  收藏  举报