最长公共子序列(LCS)是一个很经典的问题。 给2个字符串s1,s2,求LCS(s1,s2). 用d[i][j]]表示s1[1-i]和是s2[1-j]的LCS.
if(s1[i]==s2[j]) d[i][j]=d[i-1][j-1]+1
else d[i][j]=max(d[i-1][j],d[I][j-1]).
这是一个1D/1D的动态规划问题,复查度为O(n*n)。 考虑如下特殊的LCS问题.求2个全排列p1,p2的LCS。
这个问题的特殊性在于,每个数只出现一次。我们观察转移方程,只有在是p1[i]==p2[j] 的时候才会出现+1的转移。那么其实有
我们用pari[i]表示s1[i]在s2[i]中出现的位置。 可以得出d[i][pari[i]]=max{d[l'][j']}+1 .i>l;.pari[i]>j'。这样可以用一个数据结构在维护,复杂度可以降低到nlogn级别
例1:
uva10635
#include <set> #include <map> #include <queue> #include <stack> #include <cmath> #include <string> #include <cctype> #include <cstdio> #include <cstdlib> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int inf = 0x3fffffff; const int mmax = 250*250+10; map<int,int>q; int p1[mmax],p2[mmax]; int pos[mmax]; int C[mmax]; int n; int low_bit(int x) { return x&(-x); } void update(int pos,int val) { for(int i=pos;i<=n;i+=low_bit(i)) C[i]=max(C[i],val); } int get_max(int x) { int ans=0; for(int i=x;i>0;i-=low_bit(i)) ans=max(ans,C[i]); return ans; } int main() { int t,ca=0; cin>>t; while(t--) { int d,l1,l2; q.clear(); scanf("%d %d %d",&d,&l1,&l2); l1++,l2++; for(int i=1;i<=l1;i++) scanf("%d",&p1[i]); for(int i=1;i<=l2;i++) { scanf("%d",&p2[i]); q[p2[i]]=i; } memset(C,0,sizeof C); n=l2; int ans=0; for(int i=1;i<=l1;i++) { if(!q.count(p1[i])) continue; int y=q[p1[i]]; int tmp=get_max(y)+1; ans=max(ans,tmp); update(y,tmp); } printf("Case %d: %d\n",++ca,ans); } return 0; }
这题还有一种特别的解法,用映射的手段,吧s1的映射成,1,2,3,4.。。。n,求s2 的最长上升序列。用n*logn的算法
#include <set> #include <map> #include <queue> #include <stack> #include <cmath> #include <string> #include <cctype> #include <cstdio> #include <cstdlib> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int inf = 0x3fffffff; const int mmax = 250*250+10; int p1[mmax],p2[mmax]; int dp[mmax]; map<int,int>q; int main() { int t,ca=0; cin>>t; while(t--) { int d,l1,l2; q.clear(); scanf("%d %d %d",&d,&l1,&l2); l1++,l2++; for(int i=1;i<=l1;i++) { scanf("%d",&p1[i]); q[p1[i]]=i; } for(int i=1;i<=l2;i++) { int x; scanf("%d",&x); if(!q.count(x)) p2[i]=0; else p2[i]=q[x]; } int len=1; dp[1]=p2[1]; for(int i=2;i<=l2;i++) { int l=1,r=len+1; int mid=(l+1)>>1; while(l<r) { mid=(l+r)>>1; if(dp[mid]<p2[i]) l=mid+1; else r=mid; } dp[r]=p2[i]; if(r==len+1) len++; } printf("Case %d: %d\n",++ca,len); } return 0; }
解法自行YY,给出AC代码
#include <set> #include <map> #include <queue> #include <stack> #include <cmath> #include <string> #include <cctype> #include <cstdio> #include <cstdlib> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int inf = 0x3fffffff; const int mmax =100010; map<int,int>q; int p1[mmax],p2[mmax]; int next[mmax]; int C[mmax]; int n; int low_bit(int x) { return x&(-x); } void update(int pos,int val) { for(int i=pos;i<=n;i+=low_bit(i)) C[i]=max(C[i],val); } int get_max(int x) { int ans=0; for(int i=x;i>0;i-=low_bit(i)) ans=max(ans,C[i]); return ans; } int main() { while(cin>>n) { n*=5; q.clear(); for(int i=1;i<=n;i++) scanf("%d",&p1[i]); memset(next,-1,sizeof next); for(int i=1;i<=n;i++) { scanf("%d",&p2[i]); if(!q.count(p2[i])) q[p2[i]]=i; else { next[i]=q[p2[i]]; q[p2[i]]=i; } } memset(C,0,sizeof C); int ans=0; for(int i=1;i<=n;i++) { if(!q.count(p1[i])) continue; int y=q[p1[i]]; while(y+1) { int tmp=get_max(y-1)+1; ans=max(ans,tmp); update(y,tmp); y=next[y]; } } printf("%d\n",ans); } return 0; }