正题
题目链接:https://loj.ac/p/6564
题目大意
给两个序列a,ba,ba,b求它们的最长公共子序列。
1≤n,m,ai,bi≤7×1041\leq n,m,a_i,b_i\leq 7\times 10^41≤n,m,ai,bi≤7×104
解题思路
无意间看到的一个bitsetbitsetbitset科技。
首先设fi,jf_{i,j}fi,j表示aaa串匹配到第iii个bbb串匹配到第jjj个时的最长长度,做过dpdpdp套dpdpdp的应该知道fi,jf_{i,j}fi,j的性质。
0≤fi,j−fi,j−1≤10\leq f_{i,j}-f_{i,j-1}\leq 10≤fi,j−fi,j−1≤1
基本的思路就是设010101矩阵MMM满足fi,j=∑k=1jMi,kf_{i,j}=\sum_{k=1}^jM_{i,k}fi,j=∑k=1jMi,k然后用bitsetbitsetbitset优化转移
然后考虑一下怎么转移,我们先预处理出ppp数组其中pip_ipi表示数字iii出现的位置集合
我们的转移要把MMM中的111尽量的往前移动并且看能否加上一个新的111。
假设现在的字符是ccc,那么我们将使用pcp_cpc进行转移。
我们把MMM中每个111作为结尾分成若干段(最后的000也是一段,顺序是从低位到高位)。
那么对于一段中如果这一段pcp_cpc有111那么我们就取最前面的那个111,这样因为前面假设有jjj个111那么这次就匹配pcp_cpc最前面的那个作为j+1j+1j+1。
但是我们显然不可能一段一段做,我们可以考虑怎么把这个操作转成位运算🤔。
考虑一下我们平时是怎么取一个010101串的第一位的,我们有lowbit(x)=((x−1)xorx)andxlowbit(x)=((x-1)\ xor\ x)\ and\ xlowbit(x)=((x−1) xor x) and x对吧。
发现这里每段分开取实际上难实现的地方只有x−1x-1x−1,考虑怎么实现这个问题。
因为111是段的末尾,所以每一段的开头前面都是111,所以如果我们让MMM左移一位那么就变成开头是111了(需要注意补上第一段的111,所以应该是(M<<1)+1(M<<1)+1(M<<1)+1)
最后来说是
M=(X−(M<<1)+1)xorXandXM=(X-(M<<1)+1)\ xor\ X\ and\ XM=(X−(M<<1)+1) xor X and X
这样我们就完成了MMM的转移,因为需要位运算,所以需要手写bitsetbitsetbitset。
时间复杂度O(nmω)O(\frac{nm}{\omega})O(ωnm)
我是看这篇博客学的,看不懂的可以去看下:https://www.cnblogs.com/-Wallace-/p/bit-lcs.html
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ull unsigned long long
using namespace std;
const int N=7e4+10;
int n,m,L;
struct bitset{ull t[N/64+5];bitset(){memset(t,0,sizeof(t));return;}void set(int p){t[p>>6]|=(1ull<<(p&63));return;}void shift(){ull last=0;for(int i=0;i<L;i++){ull cur=t[i]>>63;(t[i]<<=1)|=last;last=cur;}return;}int count(){int ans=0;for(int i=0;i<L;i++){ull x=t[i];while(x)x-=(x&-x),ans++;}return ans;}bitset& operator=(const bitset &b){memcpy(t,b.t,sizeof(t));return *this;}bitset& operator|=(const bitset &b){for(int i=0;i<L;i++)t[i]|=b.t[i];return *this;}bitset& operator&=(const bitset &b){for(int i=0;i<L;i++)t[i]&=b.t[i];return *this;}bitset& operator^=(const bitset &b){for(int i=0;i<L;i++)t[i]^=b.t[i];return *this;}
}p[N],f,g;
bitset operator-(const bitset &a,const bitset &b){bitset tmp;ull last=0;for(int i=0;i<L;i++){ull cur=(a.t[i]<b.t[i]+last);tmp.t[i]=a.t[i]-b.t[i]-last;last=cur;}return tmp;
}
int main()
{scanf("%d%d",&n,&m);L=(n>>6)+1;for(int i=1,c;i<=n;i++)scanf("%d",&c),p[c].set(i);for(int i=1,c;i<=m;i++){scanf("%d",&c);(g=f)|=p[c];f.shift();f.set(0);f=g-f;f^=g;f&=g;}printf("%d",f.count());return 0;
}