用f[i][j]来表示将a的前i个字符操作成b的前[j]个字符的最少操作次数。
对于f[i][j]:我们之前已有以下数据
如果a[i]=b[j]:那么我们在f[i-1][j-1]的基础上不需要做任何操作即可完成。步数=f[i-1][j-1].
如果a[i]!=b[j]:
那么可以在f[i-1][j-1]的基础上把a[i]修改为b[j],步数f[i-1][j-1]+1;
或者在f[i-1][j]的基础上将a[i]删除,步数f[i-1][j]+1;
或者在f[i][j-1]的基础添加b[j],步数f[i][j-1]+1;
在所有步数中选出最小值即可。
#include<stdio.h>
#include<string.h>
int main()
{
char a[2005],b[2005];
int la,lb,f[2005][2005];
scanf("%s",a+1);
la=strlen(a+1);
scanf("%s",b+1);
lb=strlen(b+1);
for(int i=1;i<=la;i++) f[i][0]=i;
for(int i=0;i<=lb;i++) f[0][i]=i;
for(int i=1;i<=la;i++)
{
for(int j=1;j<=lb;j++)
{
int min=99999;
if(a[i]==b[j])
{
f[i][j]=f[i-1][j-1];
continue;
}
if(min>f[i-1][j]+1) min=f[i-1][j]+1;
if(min>f[i][j-1]+1) min=f[i][j-1]+1;
if(min>f[i-1][j-1]+1) min=f[i-1][j-1]+1;
f[i][j]=min;
}
}
printf("%d",f[la][lb]);
return 0;
}
要满足序列是某序列的子集,这个子序列的元素应该在全序列中的位置(下标)依次递增,那么要找出a,b的公共子序列,其中所有元素在a,b中的下标一次增大。也就是说,要从b中找出a的子序列,只要满足其在a中的下标递增即可。所以我们记录b中元素在a中出现的下标。我们用a[i]表示数字i在a中的位置,a中的递增序列即为公共子序列,接下来便求a的最长增序列。
用f[i]表示前[i]个数字最大增序列的最后一个数,即最大数.对于某一个,如果a[i]>a[i-1],那么可以形成新的更长的增序列。否则,我们需要找到一个比a[i]小的最大的数,再将其a[i]排在其后面又得到新序列,
#include<stdio.h>
int main()
{
int a[100005],f[100005],map[100005],n,num,l,r,mid,len=1;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&num);
a[num]=i;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&num);
map[i]=a[num];
f[i]=999999;
}
f[1]=map[1];
for(int i=2;i<=n;i++)
{
l=1;r=len;
if(map[i]>f[len])
{
len++;
f[len]=map[i];
}
else
{
while(l<r)
{
mid=(l+r)/2;
if(map[i]<f[mid]) r=mid;
else l=mid+1;
}
f[l]=map[i]<f[l]?map[i]:f[l];
}
}
printf("%d",len);
return 0;
}