codevs2245 股票趋势
原题地址:http://codevs.cn/problem/2245/
题意:
T组数据。
给出两个由大写字母组成的字符串序列,计算两串序列的相似度,且还有一个限制,兩个相似点( 相同字母) 的前后间距不能太远,否则相似度会被扭曲。
将此问题定义为「有间距限制的最长共同子序列」(GLCS, gapped longest common subsequence)问题。
假设第一个序列称为α,第二个序列称为β。例如,α =“ACBDCAA”,β=“ADDBCDBAC” 。兩者在无间距限制的情形下,其 LCS 可为“ADCA” , “ABCA” ,或“ACBC” ,长度为4。假设间距限制如下:
A 2, B 0, C 3, D 0
上述间距之意义为,如果字母A 被选入LCS 中,则与其前一个被选入的字母之间,在α序列最多只能有2 个未被选入的字母,在β序列亦同。α与β在上述间距限制的情形,GLCS 可为“ACA” 或“ACC” ,长度为3。
对于无间距限制的情形,可将每个字母的间距视为无限大。本题的答案只要输出GLCS 的长度即可。
数据范围
1 ≤ T≤ 5
每个串的长度<=800
0<=限制间距<=400
题解:
最初想法:
定义dp[i][j]为以A的前i个字符与B的前j个字符且以 i/j 结尾的LCS
dp[i][j]=max( dp[sta~eda][atb~edb] +1)
sta=max(i-lim[i]-1,0), eda=i-1
stb=max(j-lim[j]-1,0), edb=j-1
但这个转移方程是O(n^4)的。
注意到转移方程等价于求矩形的最大值并带修改。
于是考虑二维线段树O(n^2 logn^2)
其实就是两维,第一维的每个节点对应第二维的一棵树。
空间动态开。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#define LL long long
using namespace std;
const int N=805;
const int inf=0x3f3f3f3f;
char A[N],B[N];
int la,lb,lim[30],T,dp[N][N],root=0,tail=0;
struct node
{
node(int ls=0,int rs=0,int mx=0,int root=0){}
int ls,rs,mx,root;
}tr[20000000];
int query(int &nd,int lf,int rg,int L ,int R)
{
if(!nd)
{
nd=++tail;
tr[nd]=tr[0];
}
if(L<=lf&&rg<=R) return tr[nd].mx;
int mid=(lf+rg)>>1;
int mx=0;
if(L<=mid) mx=max(mx,query(tr[nd].ls,lf,mid,L,R));
if(R>mid) mx=max(mx,query(tr[nd].rs,mid+1,rg,L,R));
return mx;
}
int query(int &nd,int lf,int rg,int L,int R,int l,int r)
{
if(!nd)
{
nd=++tail;
tr[nd]=tr[0];
}
//
if(L<=lf&&rg<=R) return query(tr[nd].root,0,lb,l,r);
int mid=(lf+rg)>>1;
int mx=0;
if(L<=mid) mx=max(mx,query(tr[nd].ls,lf,mid,L,R,l,r));
if(R>mid) mx=max(mx,query(tr[nd].rs,mid+1,rg,L,R,l,r));
return mx;
}
void update(int nd)
{
int ls=tr[nd].ls; int rs=tr[nd].rs;
tr[nd].mx=max(tr[nd].mx,max(tr[ls].mx,tr[rs].mx));
}
void modify(int &nd,int lf,int rg,int pos,int val)
{
if(!nd)
{
nd=++tail;
tr[nd]=tr[0];
}
if(lf==rg)
{
tr[nd].mx=max(tr[nd].mx,val);
return;
}
int mid=(lf+rg)>>1;
if(pos<=mid) modify(tr[nd].ls,lf,mid,pos,val);
else modify(tr[nd].rs,mid+1,rg,pos,val);
update(nd);
}
void modify(int &nd,int lf,int rg,int a,int b,int val)
{
if(!nd)
{
nd=++tail;
tr[nd]=tr[0];
}
modify(tr[nd].root,0,lb,b,val);
if(lf==rg) return;
int mid=(lf+rg)>>1;
if(a<=mid)
modify(tr[nd].ls,lf,mid,a,b,val);
else modify(tr[nd].rs,mid+1,rg,a,b,val);
}
int main()
{
freopen("glcs.in","r",stdin);
freopen("glcs.out","w",stdout);
scanf("%s",A+1); scanf("%s",B+1);
la=strlen(A+1); lb=strlen(B+1);
scanf("%d",&T);
for(int tt=1;tt<=T;tt++)
{
int ans=0;
memset(lim,-1,sizeof(lim));
memset(dp,0,sizeof(dp));
tail=0; tr[0].ls=tr[0].rs=tr[0].mx=tr[0].root=0;root=0; tr[1]=tr[0];
while(1)
{
char ss[5];
scanf("%s",ss);
if(ss[0]=='$') break;
int len;
scanf("%d",&len);
lim[ss[0]-'A']=len;
}
for(int i=1;i<=la;i++)
{
for(int j=1;j<=lb;j++)
{
if(A[i]!=B[j]) dp[i][j]=0;
else
{
int sta,stb,eda,edb;
if(lim[A[i]-'A']==-1) sta=0;
else sta=max(0,i-lim[A[i]-'A']-1);
if(lim[B[j]-'A']==-1) stb=0;
else stb=max(0,j-lim[B[j]-'A']-1);
eda=i-1; edb=j-1;
int ret=0; //dp[sta~eda][atb~edb]
if(sta<=eda) ret=query(root,0,la,sta,eda,stb,edb);
dp[i][j]=ret+1;
modify(root,0,la,i,j,dp[i][j]);
ans=max(ans,dp[i][j]);
}
}
}
printf("%d",ans);
if(tt!=T) printf(" ");
}
return 0;
}

本文探讨了一个特定的字符串匹配问题——有间距限制的最长公共子序列(GLCS)。通过使用二维线段树优化算法的时间复杂度,解决了在间距限制下找到两个字符串的最长公共子序列的问题。
5221

被折叠的 条评论
为什么被折叠?



