【CodeCraft比赛】Problem 7——X-man(最长公共子串LCS变种)

题目:点击打开链接

Question 7

Problem Statement

Dr. Charles Xavier is trying to check the correlation between the DNA samples of Magneto and Wolverine. Both the DNAs are of length N, and can be described by using all integers between 1 to N exactly once. The correlation between two DNAs is defined as the Longest Common Subsequence of both the DNAs.
Help Dr. Xavier find the correlation between the two DNAs.

Input :

First line of input contains number of Test Cases T.
ach test case starts with an integer N, size of DNA.
Next two lines contains N integers each, first line depicting the sequence of Magneto's DNA and second line depicting Wolverine's DNA.

Output :

For each test case print one integer, the correlation between the two DNAs.

Sample Input :

2
2
1 2
2 1
3
1 2 3
1 3 2

Sample Output :

1
2

Constraints :

1 ≤  T  ≤ 10 
1 ≤  N  ≤ 100000 
Memory Limit : 32 MB 
Time Limit : 2s

这个题在题干里的描述便基本说明了是LCS,但如果不注意细节的话会掉入陷阱不能自拔,徒增TLE。

首先,我们知道LCS的一般解法是DP,公式如下。

但是首先应该注意到这个题的数据规模——100000,如果是十万的话,二维数组是不可以的,编译的时候便会提示数组过大。所以使用《算法设计与信息学竞赛》中的思想。用一个滚动的一位数组代替原二维数组,写出如下代码,但是仍然TLE。

#include <iostream>
#include <cstring>
#include <string>
#include <stdio.h>
using namespace std;

int adna[100001];
int bdna[100001];
int tarnum;

//int matix[100005][100005]; 二维数组会MLE
int matix1[100001];
int matix2[100001];

int max(int a,int b) 
{
	return a>b?a:b;
}

void lcs()
{
	for(int z=0;z<tarnum;z++)
	{
		matix1[z]=0;
		matix2[z]=0;
	}
	for(int i=0;i<tarnum;i++)
	{
		for(int j=0;j<tarnum;j++)
		{
			if(adna[i]==bdna[j])
			{
				matix2[j+1]=matix1[j]+1;
			}
			else
			{
				int res=max(matix1[j+1],matix2[j]);
				matix2[j+1]=res;
			}
		}
		for(int k=0;k<=tarnum;k++)
		{
			matix1[k]=matix2[k];
		}			
	}
}

int main()
{
	int testcase;
	cin>>testcase;
	while(testcase--)
	{
		scanf("%d",&tarnum);
		for(int i=0;i<tarnum;i++)
		{
			scanf("%d",&adna[i]);
		}
		for(int j=0;j<tarnum;j++)
		{
			scanf("%d",&bdna[j]);
		}
		lcs();
		printf("%d\n",matix2[tarnum]);
	}
	return 0;
}
由于数据过大,O(MN)的效率依然会超时。但经过思考可以发现,题目中要求排序的DNA,每项都是不同的,且注意这句: and can be described by using all integers between 1 to N exactly once
这句话说明了序列M总可以按从小到大进行排列,N中没出现过的元素设置为0,这样直接求序列N的递增序列即可(二分法),在哈尔滨理工大学的OJ中也有一道相似的题可供参考( HBUST1598),AC过程如下,时间效率为O(n*LgN):

#include<stdio.h>
#include<string.h>
 
 const int MAX = 100001;
 int A[MAX];
 int B[MAX];
 
 int main()
 {
     int t;
     int p;
     int q;
     int res;
     scanf("%d", &t);
 
     while (t--)
     {
         int x;
         int provi[MAX];
         scanf("%d", &p);
 
         memset(provi, 0, sizeof(provi));
 
         for (int i=1; i<=p; i++)
         {
             scanf("%d", &x);
             provi[x] = i;
         }
         int k = 0;
         for (int i=1; i<=p; i++)
         {
             scanf("%d", &x);
             if (provi[x] != 0)
             {
                 A[k++] = provi[x];
             }
         }
         B[0] = A[0];
         res = 1;
         for (int i=1; i<k; i++)
         {
             int left = 0;
             int right = res;
             while (left < right)
             {
                 int mid = (left + right) / 2;
                 if (A[i] > B[mid])
                 {
                     left = mid + 1;
                 }
                 else
                 {
                     right = mid;
                 }
             }
             B[left] = A[i];
             if (left >= res)
             {
                 res++;
             }
         }
         printf("%d\n", res);
     }
     return 0;
 }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值