[NOIP2017模拟]Bovine Genomics

题目描述
农夫约翰最近发现他的有些奶牛会画画,刚刚学完小学生物的他立刻开始研究起了这些奶牛的 DNA。
具体的,约翰有 N 头会画画的奶牛,N 头不会画画的奶牛,这些奶牛的 DNA 可以用 M 个字符来描述,每个字符是 A、C、G、T中的一个
比如说以下是 N=3,M=8 的一个示例:
位置: 1 2 3 4 5 6 7 8(哎呀对歪了)

会画画的牛 1: A A T C C C A T
会画画的牛 2: A C T T G C A A
会画画的牛 3: G G T C G C A A

不会画画的牛 1: A C T C C C A G
不会画画的牛 2: A C T C G C A T
不会画画的牛 3: A C T T C C A T

约翰发现,他只需要看[2,5]这个区间的 DNA就可以知道一头牛会不会画画(也就是说,没有一头会画画的牛的这个区间的 DNA是和某一头不会画画的牛的这个区间的 DNA相同的),他相信一头牛是否会画画是由这个区间的 DNA决定的,他称这样的区间为“决定区间”。
现在给你所有牛的 DNA,求最短的决定区间的长度。

输入格式
第一行两个数 N,M(N<=500;M<=500)。
接下来N 行,每行 M个字符,描述 N 头会画画的牛的DNA。
接下来N 行,每行 M个字符,描述 N 头不会画画的牛的 DNA。

输出格式
输出最短的决定区间的长度。

样例数据
输入
3 8
AATCCCAT
ACTTGCAA
GGTCGCAA
ACTCCCAG
ACTCGCAT
ACTTCCAT
输出
4

备注
【数据范围】
40% 的数据:N,M<=100。
100% 的数据:N,M<=500。

分析:数据很小,二分枚举长度,然后在每一只牛的DNA中从头开始截取这么长,把会画画的丢进哈希表,用不会画画的去匹配,如果满足没有一个相同的就返回true,继续二分找更短的;不然依次平移一个字符再次计算,如果到最后都没有满足false,就返回,继续二分找更长的。(hash差不多忘光了,回头写个hash总结吧)

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int N=505;
const long long p=31,H=23333;//听说进制用31进制最佳(听说而已),但是23333是质数这是真的!
int n,m,ans,tot,hash[H],nxt[N];
unsigned long long mi[N],num[N<<1][N],val[N*N];
char s[N<<1][N];

void prework()
{
    mi[0]=1;
    for(int i=1;i<=m+1;++i)
        mi[i]=mi[i-1]*p;

    for(int i=1;i<=n*2;++i)
        for(int j=1;j<=m;++j)
            num[i][j]=num[i][j-1]*p+s[i][j]-'A'+1;
}

void insert(unsigned long long x)
{
    int y=x%H;
    for(int v=hash[y];v;v=nxt[v])
        if(val[v]==x)
            return;

    nxt[++tot]=hash[y];
    hash[y]=tot;
    val[tot]=x;
}

bool find(unsigned long long x)
{
    long long y=x%H;
    for(int v=hash[y];v;v=nxt[v])
        if(val[v]==x)
            return true;
    return false;
}

bool check(int len)
{
    unsigned long long x;
    for(int i=1;i<=m-len+1;++i)//在DNA中依次平移
    {
        memset(hash,0,sizeof(hash));
        tot=0;
        int bz=1;
        for(int j=1;j<=n;++j)//把会画画的整个一段丢进hash表
        {
            x=num[j][i+len-1]-num[j][i-1]*mi[len];
            insert(x);
        }

        for(int j=n+1;j<=2*n;++j)//用不会画画的这一段去匹配
        {
            x=num[j][i+len-1]-num[j][i-1]*mi[len];
            if(find(x))
            {
                bz=0;
                break;
            }
        }

        if(bz)
            return true;
    }

    return false;
}

int main()
{
    freopen("cownomics.in","r",stdin);
    freopen("cownomics.out","w",stdout);

    int len;
    unsigned long long x;
    n=getint();m=getint();
    for(int i=1;i<=n*2;++i)
        scanf("%s",s[i]+1);

    prework();

    int l=1,r=m;
    while(l<=r)//二分查找
    {
        int mid=l+r>>1;
        if(check(mid))
            r=mid-1;
        else
            l=mid+1;
    }

    cout<<l<<endl;
    return 0;
}

本题结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值