基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 'A'
、'C'
、'G'
和 'T'
之一。
假设我们需要调查从基因序列 start
变为 end
所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。
- 例如,
"AACCGGTT" --> "AACCGGTA"
就是一次基因变化。
另有一个基因库 bank
记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。(变化后的基因必须位于基因库 bank
中)
给你两个基因序列 start
和 end
,以及一个基因库 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
start
、end
和bank[i]
仅由字符['A', 'C', 'G', 'T']
组成
题解:
/**
* @description: 广度优先 TC:O(n^2) SC:O(n^2)
* @author: JunLiangWang
* @param {*} startGene 开始基因序列字符串
* @param {*} endGene 结束基因序列字符串
* @param {*} bank 基因库数组
* @return {*}
*/
function bfs(startGene, endGene, bank) {
/**
* 本方案使用广度优先
*/
// 将基因库数组转换成map,方便后续查询
let bankMap = new Map();
for (let item of bank) bankMap.set(item, true);
// 定义队列
let quene = [startGene],
// 记录变化次数
count = 0,
// 记录已经变化过的基因,防止重复变化(剪枝)
recordMap = new Map()
// 遍历队列元素
while (quene.length) {
// 获取该层元素数量
let size = quene.length
// 遍历该层元素
while (size--) {
// 出队
let gene = quene.shift()
// 如果当前基因等于结束基因,变化结束,返回次数
if (gene == endGene) return count;
// 基因8个字符依次遍历,改变其字符,此处不能单纯根据
// gene与endGene的字符不一致,然后将gene的字符变为
// endGene的字符,因为有可能变化后的基因在bank中并不
// 存在。
for (let i = 0; i < 8; i++) {
for (let item of ['A', 'C', 'G', 'T']) {
// 如果字符相同,则跳过
if (gene[i] == item) continue;
// 替换字符
let geneArray = gene.split('')
geneArray[i] = item
newGene = geneArray.join('')
// 如果变化后的基因在bank中存在,且为遍历过,则将其放入队列中
if (bankMap.get(newGene) && !recordMap.get(newGene)) {
quene.push(newGene)
recordMap.set(newGene, true);
}
}
}
}
// 每层每个元素都仅会变化一次,因此遍历完成该层级元素,
// 变化次数+1
count++;
}
// 变化完成未变为end基因,返回-1
return -1
}