zoukankan      html  css  js  c++  java
  • TJOI2017 DNA 和 BJOI2015 隐身术

    DNA

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

    对于 (100\%) 的数据,(S_0,S) 的长度不超过 (10^5)(0lt Tleq 10)

    分析

    https://106960.blog.luogu.org/solution-p3763

    随便翻的时候看到了这道题。hash好题,枚举每个位置判断一下能否在三次以内匹配就行了。

    时间复杂度(O(nlog m))

    co int N=1e5+1,D=131;
    char a[N],b[N];
    int n,m;
    ull p[N],f[N],g[N];
    ull ask(ull*f,int l,int r){
    	return f[r]-f[l-1]*p[r-l+1];
    }
    int lcp(int x,int y,int r){
    	int l=0;
    	while(l<r){
    		int mid=l+r+1>>1;
    		if(ask(f,x,x+mid-1)==ask(g,y,y+mid-1)) l=mid;
    		else r=mid-1;
    	}
    	return l;
    }
    bool check(int x){
    	int r=x+m-1,y=1;
    	for(int i=0;i<3;++i){
    		int t=lcp(x,y,m-y+1);
    		x+=t+1,y+=t+1;
    		if(y>m) return 1;
    	}
    	return ask(f,x,r)==ask(g,y,m);
    }
    void DNA(){
    	scanf("%s",a+1),n=strlen(a+1);
    	scanf("%s",b+1),m=strlen(b+1);
    	if(n<m) return puts("0"),void();
    	for(int i=1;i<=n;++i) f[i]=f[i-1]*D+a[i];
    	for(int i=1;i<=m;++i) g[i]=g[i-1]*D+b[i];
    	int ans=0;
    	for(int i=1;i+m-1<=n;++i)if(check(i)) ++ans;
    	printf("%d
    ",ans);
    }
    int main(){
    	p[0]=1;for(int i=1;i<N;++i) p[i]=p[i-1]*D;
    	for(int t=read<int>();t--;) DNA();
    	return 0;
    }
    

    隐身术

    给定两个串(A)(B)。请问(~B~)中有多少个非空子串和(~A~)的编辑距离不超过(K)

    所谓“子串”,指的是(~B~)中连续的一段。不同位置的内容相同的子串算作多个。两个串之间的“编辑距离”指的是把一个串变成另一个串需要的最小的操作次数,每次操作可以插入、删除或者替换一个字符。

    (100\%)的数据,(Kleq 5),两个字符串均非空,长度和小于(10^5)

    题解

    http://jklover.hs-blog.cf/2020/05/27/bzoj-4340-隐身术/#more

    SAM + dfs 爆搜.

    枚举 B 的子串左端点 (t) ,即考虑所有是以 (t) 开头的后缀的前缀.

    (k) 较小,可爆搜之,只要保证每次 (k) 都在减少, dfs 的层数就是 (O(k)) 的.

    具体地,设状态 ((x,y,z)) 表示串 (A) 匹配到了位置 (x) , 串 (B) 匹配到了位置 (y) ,还剩 (z) 次修改机会.

    (A_x=B_y) ,则我们需要先跳过一段连续的可以匹配的段,这样接下来就一定会消耗一次修改机会.

    在 SAM 上询问这两个位置的 LCP ,将其跳过即可.

    (A_x eq B_y) ,则必须使用修改操作,可以将 (B_y) 删掉,或在 (B) 中加入一个 (A_x) ,或将 (B_y) 改成 (A_x) .

    这三种操作分别转移到 ((x,y+1,z-1),(x+1,y,z-1),(x+1,y+1,z-1)) .

    当某一个串匹配完成时,由于可能还剩下了若干修改操作,合法的前缀是一段区间,差分打上标记即可.

    时间复杂度 (O(nlog n+ncdot k^3)) .

    CO int N=2e5+10;
    char A[N],B[N];
    
    int last=1,tot=1;
    array<int,27> ch[N];
    int fa[N],len[N],idx[N];
    
    void extend(int c,int p){
    	int x=last,cur=last=++tot;
    	len[cur]=len[x]+1,idx[p]=cur;
    	for(;x and !ch[x][c];x=fa[x]) ch[x][c]=cur;
    	if(!x) {fa[cur]=1; return;}
    	int y=ch[x][c];
    	if(len[y]==len[x]+1) {fa[cur]=y; return;}
    	int clone=++tot;
    	ch[clone]=ch[y],fa[clone]=fa[y],len[clone]=len[x]+1;
    	fa[cur]=fa[y]=clone;
    	for(;ch[x][c]==y;x=fa[x]) ch[x][c]=clone;
    }
    
    vector<int> to[N];
    int pos[N],num,st[2*N][19],lg[2*N];
    
    void dfs(int x){
    	pos[x]=++num,st[num][0]=len[x];
    	for(int y:to[x]) dfs(y),st[++num][0]=len[x];
    }
    
    int diff[N];
    
    int main(){
    	int K=read<int>();
    	scanf("%s%s",A+1,B+1);
    	int n=strlen(A+1),m=strlen(B+1);
    	for(int i=m;i>=1;--i) extend(B[i]-'A',m-i+1);
    	extend(26,m+1);
    	for(int i=n;i>=1;--i) extend(A[i]-'A',n-i+1+m+1);
    	for(int i=2;i<=tot;++i) to[fa[i]].push_back(i);
    	dfs(1);
    	lg[0]=-1;
    	for(int i=1;i<=num;++i) lg[i]=lg[i>>1]+1;
    	for(int i=1;i<=lg[num];++i)for(int j=1;j+(1<<i)-1<=num;++j)
    		st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
    	function<int(int,int)> query=[&](int i,int j)->int{
    		if(i>n or j>m) return 0;
    		int x=pos[idx[n-i+1+m+1]],y=pos[idx[m-j+1]];
    		if(x>y) swap(x,y);
    		int k=lg[y-x+1];
    		return min(st[x][k],st[y-(1<<k)+1][k]);
    	};
    	int ans=0;
    	int L=max(n-K,1),R=min(n+K,m);
    	for(int t=1;t<=m;++t){
    		function<void(int,int,int)> dfs=[&](int x,int y,int z)->void{
    			int lcp=query(x,y);
    			x+=lcp,y+=lcp;
    			if(x>n or y>m){
    				int d=z-(n-x+1); // extra operations
    				if(d<0) return;
    				int l=max(y-t-d,1),r=min(y-t+d,m-t+1); // valid length
    				++diff[l],--diff[r+1];
    				return;
    			}
    			else if(z){
    				dfs(x+1,y,z-1);
    				dfs(x,y+1,z-1);
    				dfs(x+1,y+1,z-1);
    			}
    		};
    		dfs(1,t,K);
    		for(int i=L;i<=R;++i) diff[i]+=diff[i-1];
    		for(int i=L;i<=R;++i)if(diff[i]) ++ans,diff[i]=0;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    字节跳动--今日头条iOS客户端启动速度优化
    RSA加密
    几种浏览器
    Linux定时任务crontab无法执行
    Python报错ImportError: No Module Named Typing的解决
    微信小程序:A、B两个小程序相互跳转,出现点击A小程序底部导航栏菜单,第一次点击无法跳转B小程序,需要点击第二次才会触发跳转到B小程序
    c# core 生成随机图文验证码
    携程Apollo统一配置管理中心
    WPF程序中嵌入winForm窗体
    sqlserver 转 postgresql
  • 原文地址:https://www.cnblogs.com/autoint/p/10848414.html
Copyright © 2011-2022 走看看