LeetCode刷题记录(三十四):最小基因变化


theme: smartblue

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

最小基因变化

image.png

题目解析

题目难度:中等
题目素材解析

根据题目的描述来看,一共提供了三个参数素材:

  1. 字符串start

  2. 字符串end

  3. 字符数组bank

注意:字符串start、end的长度一直等于8,字符数组长度在0到10之间。

所有的字符串或者数组中字符的元素,均由['A', 'C', 'G', 'T'] 组成。

我的解读

题目给出了一个规则,那就是字符串start在多次变换后达到end。

变换规则:每一次变换都只改变其中一个字符;并且每次变换后的值,在bank数组中存在才有效。

这个题目的预期结果,就是在start多次变化后到达end时,需要变化的最小次数是多少。

我对这道题的分析,其实就三种场景的处理。

第一, 特殊情况,比如start与end相等、有效数组bank为空、end本身不存在与bank数组中。

第二, 正向(最短)路径,从start开始变换,到end结束;start如果可以每一次变换都接近end,直到变换成end后,最短距离也就是start和end的字符差。也是根据每次变换的不同点个数来解决。

第三, 反向路径,如果正向路径无法到达,那么可能就要走一些弯路才可能会变换到end,这里就是通过end进行反向变换,依次找寻与其不同点个数等于1的元素;这里可能找到很多条路,也就需要比对,找出最短路径,也就是最小次数。

通过这三种情况,我们就可以得到结果了,但是正向失败后,在反向也失败的话,就是无法达到了,返回-1即可。

解题思路

根据题目解读,也大概来说一下解题思路。

第一步, 判断特殊情况:相同、数组为空、end不存在于bank数组中。

第二步, 正向路径尝试;如果尝试成功,这直接返回start和end的不同点。这里我也是单独写了一个方法,就是isNot方法,获取两个字符串的不同点个数;这个方法在整个程序中也一直都在使用。

第三步, 正向路径尝试方法中,是一个双重循环,外层循环控制不同点个数的减少,内层循环控制获取下一个符合要求的元素;

(这里通过判断元素分别和start、end的不同点来判断的。)

正向方式下,建设start和end的不同点为4,那么下一个元素和start的不同点就应该是1,和end的不同点就应该是3。

以此一直循环下去,就能获取正确的变换路径了,但不是为了路径,只是为了是否能够成功。

第四步, 如果正向方式无法变换,那么就需要考虑,是不是可以通过绕路的方式来变换到end了,这时候就是要通过反向来实现了。

第五步, 反向方法中,是一个三重循环,最外层循环用来标记起点,中层循环用来计算是否能够走到终点或者走到终点的步数,最内层循环用来计算下一个符合要求的节点。

和第三步一样,都需要判断哪个节点才能符合要求,但是规则略有不同,反向是从end开始的,因为在中途可能会绕路,所以只能来判断和end的不同点,还有一个条件,就是不能是上一个旧元素。

符合要求的元素,需要替换掉end作为下一次循环的素材,并且将本次end的值记录为旧元素。

类似于穷举法,多次循环下来,就可以得到正确的结果了;在循环中,如果遇到路径不符合要求的,则直接停止返回。

最后在最外层循环中只保留最小的路径距离即可。

代码

```java class Solution { public int minMutation(String start, String end, String[] bank) { if(start.equals(end)){ return 0; } if(bank.length == 0){ return -1; } //判断是否存在 boolean f = false; for(String s : bank){ if(end.equals(s)){ f = true; } } if(!f){ return -1; } int isNot = isNot(start, end); if(z(start, end, bank, isNot)){ return isNot; } return f(start, end, bank); }

/**
 * 正向获取
 * @param start
 * @param end
 * @param bank
 * @param isNot
 * @return
 */
private boolean z(String start, String end, String[] bank, int isNot){
    while (isNot != 0){
        for (int i = 0; i < bank.length; i++) {
            String s = bank[i];
            if (isNot(start, s) == 1 && isNot(end, s) == isNot - 1) {
                start = s;
                isNot--;
                break;
            }
            if (i == bank.length - 1) {
                return false;
            }
        }
    }
    return true;
}

/**
 * 反向获取
 * @param start
 * @param end
 * @param bank
 * @return
 */
private int f(String start, String end, String[] bank){
    int result = -1;
    //先获取一个
    for (String s : bank) {
        if (isNot(end, s) == 1) {
            //再循环匹配
            String z = s;
            String old = end;
            int n = 0;
            while (n != -1 && isNot(start, z) != 1) {
                for (int j = 0; j < bank.length; j++) {
                    if (isNot(z, bank[j]) == 1 && !old.equals(bank[j])) {
                        old = z;
                        z = bank[j];
                        n++;
                        break;
                    }
                    if (j == bank.length - 1) {
                        n = -1;
                        break;
                    }
                }
            }
            if (result < ++n) {
                result = n;
            }
        }
    }
    return result != -1 ? ++result : -1;
}

private int isNot(String source, String target){
    int num = 0;
    for (int i = 0; i < 8; i++) {
        if(source.charAt(i) != target.charAt(i)){
            num++;
        }
    }
    return num;
}

} ```

执行结果

简直太快了,哈哈哈,没用任何结构,这很暴力美学。

image.png

Java代码本地执行

Java本地可调试代码,请参考github/Ijiran,可通过索引看到相应代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ijiran

一杯咖啡太贵,一块糖就可以

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值