BZOJ4892: [Tjoi2017]dna

Description

加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,
有这个序列的碱基序列就会表现出喜欢吃藕的性状。
但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状。
现在研究人员想知道这个基因在DNA链S0上的位置。
所以你需要统计在一个表现出吃藕性状的人的DNA序列S0上,有多少个连续子串可能是该基因。
即有多少个S0的连续子串修改小于等于三个字母能够变成S。

Input

第一行有一个数T,表示有几组数据
每组数据第一行一个长度不超过10^5的碱基序列S0
每组数据第二行一个长度不超过10^5的吃藕基因序列S

Output

共T行,第i行表示第i组数据中,在S0中有多少个与S等长的连续子串可能是表现吃藕性状的碱基序列

Sample Input

1
ATCGCCCTA
CTTCA

Sample Output

2

题解Here!
这题解法比较多。
后缀数组:

先求出后缀数组,然后对$height$数组建立$RMQ$。

于是$LCP$就可以$O(1)$求了。

我们枚举原串的每一个字符作为起点,如果相同就用$lcp$求,不相同就给记录不相同的计数器$++$。

保证不相同的计数器不超过$3$。

如果在$3$以内我们就统计答案。

注:这个好像在BZOJ上被卡常了,我也不知道为什么。。。

据说$SAM$跑得飞快,啥时候去学一学。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define MAXN 200010
using namespace std;
int n,m;
int top,sa[MAXN],rk[MAXN],height[MAXN],tax[MAXN],tp[MAXN],f[MAXN][20];
char str[MAXN];
inline int read(){int date=0,w=1;char c=0;while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}return date*w;
}
void radixsort(){for(int i=0;i<=top;i++)tax[i]=0;for(int i=1;i<=n;i++)tax[rk[i]]++;for(int i=1;i<=top;i++)tax[i]+=tax[i-1];for(int i=n;i>=1;i--)sa[tax[rk[tp[i]]]--]=tp[i];
}
void suffixsort(){top=128;for(int i=1;i<=n;i++){rk[i]=str[i];tp[i]=i;}radixsort();for(int w=1,p=0;p<n;top=p,w<<=1){p=0;for(int i=1;i<=w;i++)tp[++p]=n-w+i;for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w;radixsort();swap(tp,rk);rk[sa[1]]=p=1;for(int i=2;i<=n;i++)rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;}
}
void getheight(){for(int i=1,j,k=0;i<=n;i++){if(k)k--;j=sa[rk[i]-1];while(str[i+k]==str[j+k])k++;height[rk[i]]=k;}
}
void step(){for(int i=1;i<=n;i++)f[i][0]=height[i];for(int i=1;(1<<i)<=n;i++)for(int j=1;j+(1<<i)-1<=n;j++)f[j][i]=min(f[j][i-1],f[j+(1<<(i-1))][i-1]);
}
int query(int l,int r){int x=rk[l],y=rk[r],k;if(x>y)swap(x,y);x++;k=(int)log2(y-x+1);return min(f[x][k],f[y-(1<<k)+1][k]);
}
void work(){int ans=0;for(int i=1;i<=m*2-n;i++){int s=0;for(int j=1;j<=n-m&&s<=3;){if(str[i+j-1]!=str[m+j]){s++;j++;if(s>3)break;}else j+=query(i+j-1,m+j);}if(s<=3)ans++;}printf("%d\n",ans);
}
void init(){scanf("%s",str+1);m=strlen(str+1);str[++m]='#';scanf("%s",str+m+1);n=strlen(str+1);suffixsort();getheight();step();
}
int main(){int t=read();while(t--){init();work();}return 0;
}


后缀自动机:

(坑,未填。。。)


FFT:
$FFT$?
对!$FFT$!
你不禁想问,这题是字符串匹配,跟$FFT$八竿子打不着啊。。。

但是假如转换一下匹配的结果呢?

把两个串中的一个(为了方便,选择短的串)翻转,卷积的结果就是在文本串每一位置匹配的结果。

$A,C,T,G$分开算,以$A$为例:

让一个串中有$A$的位置为$1$,无$A$的位置为$0$,另一个串相反。

这样两串字符不同处会为结果的值贡献$1$ 。

对于$<=3$的结果个数和为答案。

玄妙的解法啊。。。

当然,$FFT$常数巨大,BZOJ日常卡常。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define MAXN (1<<19)
using namespace std;
const char f[4]={'A','C','G','T'};
int l1,l2;
int sum[MAXN];
char str_one[MAXN],str_two[MAXN];
inline int read(){int date=0,w=1;char c=0;while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}return date*w;
}
namespace FFT{int n,l,rev[MAXN];struct node{double r,i;node operator +(const node &p){return (node){r+p.r,i+p.i};}node operator -(const node &p){return (node){r-p.r,i-p.i};}node operator *(const node &p){return (node){r*p.r-i*p.i,r*p.i+i*p.r};}}a[MAXN],b[MAXN],d[MAXN];void DFT(node *a,int f){for(int i=0;i<l;i++)d[i]=a[rev[i]];for(int i=0;i<l;i++)a[i]=d[i];for(int i=2;i<=l;i<<=1){node wi=(node){cos(2.00*M_PI/i),f*sin(2.00*M_PI/i)};for(int k=0;k<l;k+=i){node w=(node){1.00,0.00},x,y;for(int j=0;j<i/2;j++){x=a[k+j];y=w*a[k+j+i/2];a[k+j]=x+y;a[k+j+i/2]=x-y;w=w*wi;}}}if(f==-1)for(int i=0;i<l;i++)a[i].r/=l;}void init(int x){/*for(n=l=1;l<x;l<<=1)n++;l<<=1;for(int i=0;i<l;i++)for(int j=0,t=i;j<n;j++,t>>=1){rev[i]<<=1;rev[i]|=t&1;}*/l=1;while (l<l1+l2) l<<=1;for (int i=0;i<l;++i)rev[i]=((i&1)?rev[i^1]+(l>>1):rev[i>>1]>>1);}void FFT(){DFT(a,1);DFT(b,1);for(int i=0;i<l;i++)a[i]=a[i]*b[i];DFT(a,-1);}void DSC(int l1,int l2){memset(rev,0,sizeof(rev));init(l1+l2);for(int num=0;num<4;num++){for(int i=0;i<=l;i++)a[i].r=a[i].i=b[i].r=b[i].i=0.00;for(int i=0;i<l1;i++)a[i].r=(str_one[i+1]!=f[num]);for(int i=0;i<l2;i++)b[i].r=(str_two[i+1]==f[num]);FFT();for(int i=l2-1;i<l1;i++)sum[i]+=(int)(a[i].r+0.5);}}
}
void work(){int ans=0;for(int i=l2-1;i<l1;i++)ans+=(sum[i]<=3);printf("%d\n",ans);
}
void init(){memset(sum,0,sizeof(sum));scanf("%s%s",str_one+1,str_two+1);l1=strlen(str_one+1);l2=strlen(str_two+1);for(int i=1;i<=(l2>>1);i++)swap(str_two[i],str_two[l2-i+1]);FFT::DSC(l1,l2);
}
int main(){int t=read();while(t--){init();work();}return 0;
}

转载于:https://www.cnblogs.com/Yangrui-Blog/p/9446494.html

BZOJ4892: [Tjoi2017]dna相关推荐

  1. [TJOI2017]DNA --- 后缀数组

    [TJOI2017]DNA 题目描述 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S, 有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个 ...

  2. [TJOI2017]DNA

    Description: 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够 ...

  3. P3763 [TJOI2017]DNA (FFT)

    原题题面 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列 SSS,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列 SSS,任意修改其中不超过 3 个碱基,依然能够 ...

  4. BZOJ 4892: [Tjoi2017]DNA(SA+RMQ / SAM)

    Description 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的 性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够 ...

  5. 洛谷P3763 [Tjoi2017]DNA 【后缀数组】

    题目链接 洛谷P3763 题解 后缀数组裸题 在BZOJ被卡常到哭QAQ #include<algorithm> #include<iostream> #include< ...

  6. P3763 [TJOI2017]DNA(SAM+dfs)

    LINK SAM写法 记得以前是用 F F T FFT FFT写的,推狮子翻来覆去- 然而这不是被 S A M SAM SAM秒掉的垃圾题吗??!! 建立 S A M SAM SAM,直接从根节点去 ...

  7. 暑假集训 ---- 字符串2 (SAM专题)

    P1368 工艺 把串插入 S A M SAM SAM 插两次,然后贪心找最小字典序即可 [AHOI2013]差异 两个串的最长公共后缀就是后缀自动机上 lca 的长度 于是把两个串反过来,对于每一个 ...

  8. 浅谈 FFT (终于懂一点了~~)

    FFT(离散傅氏变换的快速算法) FFT(Fast Fourier Transformation)是离散傅氏变换(DFT)的快速算法.即为快速傅氏变换.它是根据离散傅氏变换的奇.偶.虚.实等特性,对离 ...

  9. 2018十二月刷题列表

    Preface \(2018\)年的尾巴,不禁感慨自己这一年的蜕变只能用蜕变来形容了. 而且老叶说我们今年没的参加清北冬令营可以参加CCF在广州二中举办的冬令营,只要联赛\(390+\)就应该可以报. ...

最新文章

  1. (康托展开解释)+ NYOJ 139 我排第几个
  2. 注释(Annotation)
  3. superMap添加marker及连线
  4. 没有人会告诉您乘坐飞机时的几个事实 但是您一定要知道
  5. android 设置允许http请求_接口测试第6期:Fiddler设置开始捕获和停止捕获、HTTP报文结构,如何删除请求...
  6. Ubuntu 16.04 主题美化及常用软件安装
  7. DockOne微信分享(一三一):Juice——一种基于MesosFramework的任务云框架
  8. java如何存储一个向量到一个矩阵的一个位置_SIMD数据并行(一)——向量体系结构...
  9. QQ春节游园会被拆开11.2亿个福袋 近一半都被00后给拆了
  10. Android 布局渲染流程与卡顿优化
  11. NGN学习笔记4——软交换中的协议2—Megaco/H.248
  12. plc用c语言编程的好处,学习PLC编程的重要性
  13. 抓包工具 Fiddler 使用介绍
  14. OPPO R17忘记用户账户密码强制清除登录账号
  15. 计算机期末考试方案,初中信息技术期末考试方案.doc
  16. WIFI工具移植之IW工具移植
  17. h桥控制电机刹车_一种电机H桥制动电路的制作方法
  18. Windows/Windows Server 控制面板(Control Panel) 打开方式 通用
  19. 奇幻RPG(人物构建 与 Abstract Factory模式)
  20. JavaScript头像上传器的实现

热门文章

  1. python爬虫入门教程(三):淘女郎爬虫 ( 接口解析 | 图片下载 )
  2. 洛谷P2161 [SHOI2009]会场预约【Treap】
  3. python爬虫:淘宝图片爬虫
  4. 201671010412 郭佳 实验二 软件工程个人项目
  5. 44道JS难题,做对一半就是高手
  6. Android eSIM-LPA基于Android13的实现
  7. 介绍一下芯片的VIA pillar
  8. [python] 圆形嵌套图Circular Packing
  9. iis服务器如何修改首页,IIS7~IIS8.5删除或修改服务器协议头Server
  10. php去重和 sql 去重,原生php结果集如何去重?