最小基因变化[中等]

一、题目

基因序列可以表示为一条由8个字符组成的字符串,其中每个字符都是ACGT之一。假设我们需要调查从基因序列start变为end所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。

例如,AACCGGTT --> AACCGGTA就是一次基因变化。

另有一个基因库 bank 记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。(变化后的基因必须位于基因库bank中)。给你两个基因序列startend,以及一个基因库bank,请你找出并返回能够使start变化为end所需的最少变化次数。如果无法完成此基因变化,返回-1

注意:起始基因序列start默认是有效的,但是它并不一定会出现在基因库中。

示例 1:
输入:start = "AACCGGTT", end = "AACCGGTA", bank = ["AACCGGTA"]
输出:1

示例 2:
输入:start = "AACCGGTT", end = "AAACGGTA", bank = ["AACCGGTA","AACCGCTA","AAACGGTA"]
输出:2

示例 3:
输入:start = "AAAAACCC", end = "AACCCCCC", bank = ["AAAACCCC","AAACCCCC","AACCCCCC"]
输出:3

start.length == 8
end.length == 8
0 <= bank.length <= 10
bank[i].length == 8
startendbank[i]仅由字符['A', 'C', 'G', 'T']组成

二、代码

【1】广度优先搜索: 经过分析可知,题目要求将一个基因序列A变化至另一个基因序列B,需要满足以下条件:
1、序列A与 序列B之间只有一个字符不同;
2、变化字符只能从A, C, G, T中进行选择;
3、变换后的序列B一定要在字符串数组bank中。

根据以上变换规则,我们可以进行尝试所有合法的基因变化,并找到最小的变换次数即可。步骤如下:
1、如果startend相等,此时直接返回0;如果最终的基因序列不在bank中,则此时按照题意要求,无法生成,直接返回−1
2、首先我们将可能变换的基因 sss 从队列中取出,按照上述的变换规则,尝试所有可能的变化后的基因,比如一个AACCGGTA,我们依次尝试改变基因s的一个字符,并尝试所有可能的基因变化序列s0,s1,s2,,si,,s23,变化一次最多可能会生成3×8=24种不同的基因序列。
3、我们需要检测当前生成的基因序列的合法性si​,首先利用哈希表检测si 是否在数组bank中,如果是则认为该基因合法,否则改变化非法直接丢弃;其次我们还需要用哈希表记录已经遍历过的基因序列,如果该基因序列已经遍历过,则此时直接跳过;如果合法且未遍历过的基因序列,则我们将其加入到队列中。
4、如果当前变换后的基因序列与end相等,则此时我们直接返回最小的变化次数即可;如果队列中所有的元素都已经遍历完成还无法变成end,则此时无法实现目标变化,返回−1

class Solution {public int minMutation(String start, String end, String[] bank) {Set<String> cnt = new HashSet<String>();Set<String> visited = new HashSet<String>();char[] keys = {'A', 'C', 'G', 'T'};        for (String w : bank) {cnt.add(w);}if (start.equals(end)) {return 0;}if (!cnt.contains(end)) {return -1;}Queue<String> queue = new ArrayDeque<String>();queue.offer(start);visited.add(start);int step = 1;while (!queue.isEmpty()) {int sz = queue.size();for (int i = 0; i < sz; i++) {String curr = queue.poll();for (int j = 0; j < 8; j++) {for (int k = 0; k < 4; k++) {if (keys[k] != curr.charAt(j)) {StringBuffer sb = new StringBuffer(curr);sb.setCharAt(j, keys[k]);String next = sb.toString();if (!visited.contains(next) && cnt.contains(next)) {if (next.equals(end)) {return step;}queue.offer(next);visited.add(next);}}}}}step++;}return -1;}
}

时间复杂度: O(C×n×m),其中n为基因序列的长度,m为数组bank的长度。对于队列中的每个合法的基因序列每次都需要计算C×n种变化,在这里C=4;队列中最多有m个元素,因此时间复杂度为O(C×n×m)
空间复杂度: O(n×m),其中n为基因序列的长度,m为数组bank的长度。合法性的哈希表中一共存有m个元素,队列中最多有m个元素,每个元素的空间为O(n);队列中最多有m个元素,每个元素的空间为O(n),因此空间复杂度为O(n×m)

【2】预处理优化: 经过分析可知,题目要求将一个基因序列A变化至另一个基因序列B,需要满足一下条件:
1、A与 序列B之间只有一个字符不同;
2、变化字符只能从A, C, G, T中进行选择;
3、变换后的序列B一定要在字符串数组bank中。

已知方法一中广度优先搜索方法,我们可以对bank进行预处理,只在合法的基因变化进行搜索即可。由于题目中给定的bank基因库的长度较小,因此可以直接在对bank进行预处理,找到基因库中的每个基因的合法变换,而不需要像方法一中每次都需要去计算基因的变化序列,我们将每个基因的合法变化关系存储在邻接表adj中,每次基因变化搜索只在adj中进行即可。

class Solution {public int minMutation(String start, String end, String[] bank) {int m = start.length();int n = bank.length;List<Integer>[] adj = new List[n];for (int i = 0; i < n; i++) {adj[i] = new ArrayList<Integer>();}int endIndex = -1;for (int i = 0; i < n; i++) {if (end.equals(bank[i])) {endIndex = i;}for (int j = i + 1; j < n; j++) {int mutations = 0;for (int k = 0; k < m; k++) {if (bank[i].charAt(k) != bank[j].charAt(k)) {mutations++;}if (mutations > 1) {break;}}if (mutations == 1) {adj[i].add(j);adj[j].add(i);}}}if (endIndex == -1) {return -1;}Queue<Integer> queue = new ArrayDeque<Integer>();boolean[] visited = new boolean[n];int step = 1;for (int i = 0; i < n; i++) {int mutations = 0;for (int k = 0; k < m; k++) {if (start.charAt(k) != bank[i].charAt(k)) {mutations++;}if (mutations > 1) {break;}}if (mutations == 1) {queue.offer(i);visited[i] = true;}}        while (!queue.isEmpty()) {int sz = queue.size();for (int i = 0; i < sz; i++) {int curr = queue.poll();if (curr == endIndex) {return step;}for (int next : adj[curr]) {if (visited[next]) {continue;}visited[next] = true;queue.offer(next);}}step++;}return -1;}
}

时间复杂度: O(m×n2),其中m为基因序列的长度,n为数组bank的长度。计算合法的基因变化adj需要的时间为O(m×n2),广度优先搜索时,队列中最多有n个元素,需要的时间为O(n),因此时间复杂度为O(m×n2)
空间复杂度: O(n2),其中n为数组bank的长度。计算合法的基因变化adj需要的空间为O(n^2),队列中最多有n个元素,因此空间复杂度为O(n^2)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/238925.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端项目为什么需要 TypeScript 来强化?

什么是TypeScript? TypeScript 是一个为开发大规模应用程序而设计的语言。它是 JavaScript 的一个超集&#xff0c;包含 JavaScript 全部的功能&#xff0c;并扩展了一些新的特性。具体来说&#xff0c;TypeScript 增加了如类型注解和编译时类型检查等特性&#xff1a; let n…

淘宝通过关键字搜索商品列表API接口对接详细步骤(支持免费测试)

通过关键字搜索商品&#xff0c;批量获取到相关商品&#xff0c;这是几乎所有电商平台购物商城都有的功能。我将此功能封装为API&#xff0c;可供外部软件直接调用&#xff0c;实现通过关键字搜索淘宝商品的功能。 接口名称&#xff1a;item_search-按关键字搜索淘宝商品 请求…

微信小程序promise封装

一. 在utils文件夹内创建一个request.js 写以下封装的 wx.request() 方法 const baseURL https:// 域名 ; //公用总路径地址 export const request (params) > { //暴露出去一个函数&#xff0c;并且接收一个外部传入的参数let dataObj params.data || {}; //…

pytorch中nn.Sequential详解

1 nn.Sequential概述 1.1 nn.Sequential介绍 nn.Sequential是一个序列容器&#xff0c;用于搭建神经网络的模块被按照被传入构造器的顺序添加到容器中。除此之外&#xff0c;一个包含神经网络模块的OrderedDict也可以被传入nn.Sequential()容器中。利用nn.Sequential()搭建好…

蓝牙耳机编码方式

蓝牙耳机编码方式 蓝牙耳机的编码方式指的是蓝牙耳机如何处理和传输音频数据。主要的蓝牙编码方式包括&#xff1a; SBC (Subband Coding)&#xff1a;这是蓝牙音频的标准编码方式&#xff0c;所有蓝牙音频设备都支持。虽然它的音质不是最佳&#xff0c;但兼容性很好。 AAC (A…

【重点】【DP】5.最长回文子串|516.最长回文子序列

两个求解目标类似的题目&#xff0c;对比记忆&#xff01; 5.最长回文子串 题目 法1&#xff1a;二维DP 最基础方法&#xff01;必须掌握&#xff01; O(N^2) O(N^2) class Solution {public String longestPalindrome(String s) {int n s.length();if (n 1) {return s…

webpack之介绍

学习webpack之前&#xff0c;请先让我们大家了解一下什么是webpack&#xff1f;为什么要用webpack&#xff1f; Webpack是一个现代化的JavaScript应用程序的静态模块打包工具。它可以将多个模块打包成一个或多个静态资源文件&#xff0c;以便在浏览器中使用。 Webpack的主要功…

连几句恶语都容它不下,那是鸡肠鼠肚,有大度才能成大器。

连几句恶语都容它不下&#xff0c;那是鸡肠鼠肚&#xff0c;有大度才能成大器。

Spring Boot测试 - JUnit整合及模拟Mvc

概述 在现代软件开发中&#xff0c;测试是确保应用程序质量和稳定性的关键步骤。Spring Boot框架为开发人员提供了丰富的测试工具和集成&#xff0c;其中JUnit是最常用的测试框架之一。本文将介绍如何在Spring Boot项目中集成JUnit测试&#xff0c;以及如何使用模拟Mvc来进行W…

csrf自动化检测调研

https://github.com/pillarjs/understanding-csrf/blob/master/README_zh.md CSRF 攻击者在钓鱼站点&#xff0c;可以通过创建一个AJAX按钮或者表单来针对你的网站创建一个请求&#xff1a; <form action"https://my.site.com/me/something-destructive" metho…

一些问题/技巧的集合(仅个人使用)

目录 第一章、1.1&#xff09;前端找不到图片1.2&#xff09;1.3&#xff09;1.4&#xff09; 第二章、2.1&#xff09;2.2&#xff09;2.3&#xff09; 第三章、3.1&#xff09;3.2&#xff09;3.3&#xff09; 第四章、4.1&#xff09;4.2&#xff09;4.3&#xff09; 友情提…

系列一、GitHub搜索技巧

一、GitHub搜索技巧 1.1、概述 作为程序员&#xff0c;GitHub大家应该都再熟悉不过了&#xff0c;很多时候当我们需要使用某一项技能而又无从下手时&#xff0c;通常会在百度&#xff08;面向百度编程&#xff09;或者在GitHub上通过关键字寻找相关案例&#xff0c;比如我想学…

IU5070E线性单节锂电池充电管理IC

IU5070E是一款具有太阳能板最大功率点跟踪MPPT功能&#xff0c;单节锂离子电池线性充电器&#xff0c;最高支持1.5A的充电电流&#xff0c;支持非稳压适配器。同时输入电流限制精度和启动序列使得这款芯片能够符合USB-IF涌入电流规范。 IU5070E具有动态电源路径管理(DPPM)功能&…

如果你带着热爱专注地做些事,很多有趣的事就会随之而来。

如果你带着热爱专注地做些事&#xff0c;很多有趣的事就会随之而来。

第11章 GUI Page403~405 步骤三 设置滚动范围

运行效果&#xff1a; 源代码&#xff1a; /**************************************************************** Name: wxMyPainterApp.h* Purpose: Defines Application Class* Author: yanzhenxi (3065598272qq.com)* Created: 2023-12-21* Copyright: yanzhen…

一款外置MOS开关降压型 LED 恒流控制器应用方案

一、基本概述 TX6121 是一款高效率、高精度的降压型大功率 LED 恒流驱动控制器芯片。芯片采用固定关断时间的峰值电流控制方式&#xff0c;关断时间可通过外部电容进行调节&#xff0c;工作频率可根据用户要求而改变。 通过调节外置的电流采样电阻&#xff0c;能控制高亮度 LE…

火力发电厂电气一次部分初步设计(论文+图纸)

1 原始资料 设计电厂为中型是凝汽式发电厂&#xff0c;共4台发电机组&#xff0c;2台75MW机组&#xff0c;2台50MW机组&#xff0c;总的装机容量为250MW&#xff0c;占系统容量的比例为&#xff1a; 250/(3500250)100%6.7%<15%&#xff0c;未超过电力系统的检修备用容量和…

Jwt 如何在 springboot 项目中进行接口访问鉴权

文章目录 1 springboot 框架负责接口的拦截和放行1.1 原理1.2 思路1.3 坑: Springboot 访问了错误处理路径 /error 2 jwt token 负责携带数据和签名的生成及校验2.1 初始化2.2 设置 Header2.3 携带数据 payload2.4 签名 sign 后, 生成 token2.5 校验2.6 获取信息2.7 字段说明 3…

WebGL在教育和培训的应用

WebGL在教育和培训领域具有广泛的应用&#xff0c;其强大的图形渲染能力和跨平台性使得它成为创建交互式、视觉化的数字内容的理想选择。以下是一些WebGL在教育和培训上的应用示例&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司…