0507|433. 最小基因变化
- 一个 bfs / dfs 的应用题。
- 历年没考过,但思路不错。
方法一:bfs
const CHARS = ["A", "C", "G", "T"]
var minMutation = function (start, end, bank) {
const bankSet = new Set(bank);
const queue = [{ gene: start, step: 0 }];
// bfs
while (queue.length) {
const { gene, step } = queue.shift();
// 遍历单个基因: A, C, G, T
for (let i = 0; i < gene.length; i++) {
// 对每个基因进行替换,有3种替换方式
for (const nextChar of CHARS) {
if (gene !== nextChar) {
// 构建下一个基因序列
const next = gene.substring(0, i) + nextChar + gene.substring(i + 1);
// 下一个序列必须在基因库中,
if (bankSet.has(next)) {
// 返回结果
if (next === end) return step + 1;
// 已经使用过该基因,则从库中删去
bankSet.delete(next);
// 迭代:bfs
queue.push({ gene: next, step: step + 1 });
}
}
}
}
}
return -1;
};
方法二:dfs
var minMutation = function (start, end, bank) {
// 基因库不存在目标基因
if (bank.indexOf(end) === -1) return -1;
// 可以进行的操作,如果基因段是 'A',则可以改变为 C,G,T,共3种基因字段。
const map = new Map([['A', 'CGT'], ['C', 'AGT'], ['G', 'ACT'], ['T', 'ACG']]);
// 基因库
const bankMap = new Set(bank);
// 基因发生变化的过程
const changeMap = new Set();
return dfs(start, end, 0);
// dfs: cur 当前基因序列
function dfs(cur, target, step) {
// 找到 target 返回结果
if (cur === target) return step;
// 找到 cur 变化的下一个基因序列(24种)
const nextStatus = getNextStatus(cur);
for (let nextStr of nextStatus) {
// 过滤:基因库中不存在该序列,或者与已经变化过的序列重复
if (!bankMap.has(nextStr) || changeMap.has(nextStr)) continue;
changeMap.add(nextStr);
// 递归: 深度遍历
const ans = dfs(nextStr, target, step + 1);
// 剪枝: 找不到有效的下一个基因序列,就及时结束递归
if (ans !== -1) return ans;
}
// 找不到一个有效的下一个基因序列
return -1;
}
// 获得当前基因序列的下一次变化序列(一共有24种可能性)。
// params: geneStr 当前基因序列
// return: <nextGeneStr>[]
function getNextStatus(geneStr) {
let geneArr = Array.from(geneStr), nextStatus = [];
// 遍历基因序列的每一个字母,列出所有可以能改变,一共有 3*8=24 种
for (let i = 0; i < 8; i++) {
const temp = [...geneArr];
const nextChars = map.get(geneArr[i]); // 'A' -> 'CGT'
for (let j = 0; j < 3; j++) { // 遍历 'CGT'
temp[i] = nextChars[j]; // ['A','T','T'...] -> ['C','T','T'...]
nextStatus.push(temp.join('')); // --> .push('CTT...');
}
}
return nextStatus;
}
};