peiwenjun's blog 没有知识的荒原

LOJ6564 最长公共子序列 题解

题目描述

给定长为 \(n\) 的数字串 \(a\) 和长为 \(m\) 的数字串 \(b\) ,求 \(a\) 和 \(b\) 的最长公共子序列长度。

数据范围

  • \(1\le n,m,a_i,b_i\le7\cdot 10^4\) 。

时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{1GB}\) 。

分析

先考虑朴素的 \(\mathcal O(nm)\) 做法, \(f_{i,j}\) 表示 \(a[1\sim i]\) 和 \(b[1\sim j]\) 的最长公共子序列,转移方程:

\[f_{i,j}=\max(f_{i-1,j},f_{i,j-1},[a_i=b_j](f_{i-1,j-1}+1))\\ \]

记 \(g_{i,j}=f_{i,j}-f_{i,j-1}\) 为对 \(f\) 做行差分的结果,则答案为 \(\sum_{j=1}^mg_{n,j}\) 。

\(g_{i,j}\) 的实际含义:固定 \(a[1\sim i]\) ,新增字符 \(b_j\) 能否让 \(\texttt{LCS}\) 长度增加。

容易发现 \(g_{i,j}\in\{0,1\}\) ,考虑 bitset 优化。

对每个数字 \(i\) ,预处理它在数组 \(b\) 中出现的位置集合 \(p_i\) 。

将 \(g_{i-1}\) 分段,使得 \(g_{i-1}\) 中的每个 1 都是某段的末尾。

记 \(g_{i-1}\) 中 1 的出现位置为 \(x_1\lt x_2\lt\cdots\) ,那么将 \([x_k+1,x_{k+1}]\) 分成一段。

记 \(a_i=c\) ,那么新加字符 \(c\) 的贡献为,对 \(\forall u\in p_c\) ,将 \(u\) 所在段内末尾的 1 移到 \(u\) 所在位置。如果某段内存在多个 \(p_c\) 中的元素,则移到最靠前的一个。

原本我们需要考虑 \(b\) 的前 \(x_{k+1}\) 个字符,才能使 \(\texttt{LCS}\) 长度为 \(k+1\) 。

但加入字符 \(c\) 以后,前 \(x_k\) 个字符提供了长度为 \(k\) 的公共子序列,我们只需要在 \(x_k\) 后面选一个最靠前的字符 \(c\) 作为公共子序列的第 \(k+1\) 项。


上述过程可以等效成,对于每一段,我们仅保留 g[i-1]|p[c] 的第一个 1 。

显然我们不能直接枚举每一段去更新答案,考虑用位运算刻画。

注意到对于单个 01 串 \(x\) ,有 lowbit(x)=x&(~x+1) 。

g[i-1]|p[c] 可以看成若干个 \(x\) 拼起来,但是对每一段分别求 ~x+1 不好处理。

回顾分段方式,每一段前面的一个字符(也就是上一段的末尾字符)都是 1 ,不过第一段前面什么都没有,需要特判。因此每段 +1 等价于加上 \(2*p_{i-1}+1\) 。

完整的转移方程如下:

g[i]=(~(g[i-1]|p[a[i]])+(2*g[i-1]+1))&(g[i-1]|p[a[i]]);

由于涉及到数值加法,我们需要手写 bitset ,时间复杂度 \(\mathcal O(\frac{nm}w)\) 。

#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int maxn=7e4+5;
const ull all=(1ull<<63)-1;
int m,n,x,res;
ull g[maxn],p[maxn][maxn/63+1];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) scanf("%d",&x),p[x][i/63]|=1ull<<(i%63);
    for(int i=0;i<m;i++)
    {
        scanf("%d",&x);
        ull t=1;
        for(int j=0;j<=n/63;j++)
        {
            ull a=g[j],b=a|p[x][j];
            a=2*a+t+(all^b);///a=(2*g[i-1]+1)+~(g[i-1]|p[x])
            g[j]=a&b,t=a>>63;
        }
    }
    for(int j=0;j<=n/63;j++) res+=__builtin_popcountll(g[j]);
    printf("%d\n",res);
    return 0;
}

posted on 2023-04-20 18:49  peiwenjun  阅读(16)  评论(0)    收藏  举报

导航