zoukankan      html  css  js  c++  java
  • 【BZOJ4641】基因改造 KMP

    【BZOJ4641】基因改造

    Description

    "人类智慧的冰峰,只有萌萌哒的我寂寞地守望。"
    --TB
    TB正走在改造人类智慧基因的路上。TB发现人类智慧基因一点也不萌萌哒,导致人类普遍智商过低,为了拯救低智商人群,博爱的TB开始改造人类智慧基因。人类智慧DNA由C种人类智慧脱氧核苷酸构成(这是一种十分特殊的DNA)。TB智慧DNA片段T长度为M,她可以把另一段长度为M的人类智慧DNA片段S改造成T。改造过程中,TB可以充分发挥智慧,将任意两种人类智慧脱氧核苷酸交换(比如对于片段S=12321,交换1和2变成S=21312,交换1和4变成42324),可以无限次交换。如果S可以通过若干次交换变成T,那么就称S为"萌萌哒人类基因片段"。TB想知道对于一个长度为N的人类智慧DNA片段S[1~N],有多少个长度为M的连续子片段S[i~i+M-1],是"萌萌哒人类基因片段",并且这些"萌萌哒人类基因片段"在哪里。

    Input

    输入文件的第一行包含两个正整数case和C,分别表示数据组数和人类智慧脱氧核苷酸的种数。
    接下来3*case行,每三行表示一组数据:
    第一行一个正整数N和M,表示人类智慧DNA片段S和TB智慧DNA片段T的长度。
    第二行N个正整数,表示人类智慧DNA片段S。
    第三行M个正整数,表示TB智慧DNA片段T。
    对于所有数据数据,case=3, n,m,C<=1000000

    Output

    对于每组数据:
    第一行一个正整数tot,表示"萌萌哒人类基因片段"的个数。
    接下来一行tot个用空格隔开的正整数pos,表示"萌萌哒人类基因片段"开头所在的位置。要求从小到大输出每个pos。

    Sample Input

    3 3
    6 3
    1 2 1 2 3 2
    3 1 3
    6 3
    1 2 1 2 1 2
    3 1 3
    6 3
    1 1 2 1 2 1
    3 1 3

    Sample Output

    3
    1 2 4
    4
    1 2 3 4
    3
    2 3 4
    对于第一组数据:
    S[1~3]=121,可以先将1和2交换变成212,再将2和3交换变成313。
    S[2~4]=212,可以将2和3交换变成313。
    S[4~6]=232,可以先将2和3交换变成323,再将1和2交换变成313。

    题解:这种题多做做就差不多能掌握套路了。

    显然我们将KMP的比较函数改一改即可,不难想到用pre[i],即i上一次出现的位置来当做判相等的条件。特别地,如果pre[i]超出了S与T匹配的范围,则我们视为pre[i]=-1。细节还是需要注意一下的~

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    
    using namespace std;
    const int maxn=1000010;
    int cas,n,m,C,tot;
    int next[maxn],p1[maxn],p2[maxn],pos[maxn],last[maxn];
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    void work()
    {
    	n=rd(),m=rd(),tot=0;
    	int i,j,a;
    	memset(last,-1,sizeof(last));
    	for(i=0;i<n;i++)	a=rd(),p1[i]=(last[a]==-1)?-1:i-last[a],last[a]=i;
    	memset(last,-1,sizeof(last));
    	for(i=0;i<m;i++)	a=rd(),p2[i]=(last[a]==-1)?-1:i-last[a],last[a]=i;
    	next[0]=-1,i=0,j=-1;
    	p1[n]=p2[m]=0;
    	while(i<m)
    	{
    		if(j==-1||p2[i]==p2[j]||(p2[i]>j&&p2[j]==-1))	next[++i]=++j;
    		else	j=next[j];
    	}
    	i=j=0;
    	while(i<n)
    	{
    		if(j==-1||p1[i]==p2[j]||(p1[i]>j&&p2[j]==-1))	i++,j++;
    		else	j=next[j];
    		if(j==m)	pos[++tot]=i-m+1;
    	}
    	printf("%d
    ",tot);
    	for(i=1;i<=tot;i++)	printf("%d ",pos[i]);
    	printf("
    ");
    }
    int main()
    {
    	cas=rd(),C=rd();
    	while(cas--)	work();
    	return 0;
    }//3 3 6 3 1 2 1 2 3 2 3 1 3 6 3 1 2 1 2 1 2 3 1 3 6 3 1 1 2 1 2 1 3 1 3

     

  • 相关阅读:
    MSSQL ADO.NET
    MSSQL 详解SQL Server连接(内连接、外连接、交叉连接)
    MSSQL DBOtherSQL
    java8时间转换成字符串
    泛型
    给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
    利用栈判断有效的括号
    回文数
    service层对 @NotBlank注解起作用
    集合的使用
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7670080.html
Copyright © 2011-2022 走看看