最小限の遺伝的変化[中程度]

1. タイトル

遺伝子配列は、8文字で構成される文字列として表すことができます。各文字はACGおよびのいずれかになりますTstart遺伝子配列が変化しendたときに生じる遺伝的変化を調査する必要があるとします。遺伝的変化とは、遺伝子配列内の 1 つの文字が変化することを意味します。

たとえば、AACCGGTT--> はAACCGGTA遺伝的変化です。

すべての有効な遺伝子変化を記録するジーンバンクもあり、ジーンバンク内の遺伝子のみが有効な遺伝子配列です。(変更された遺伝子は遺伝子プールに存在する必要がありますbank)。2 つの遺伝子配列startend1 つの遺伝子ライブラリが与えられた場合、必要な変更をbank加えることができる最小の変更数を見つけて返してくださいこの遺伝的変化が完了できない場合は、 を返しますstartend-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']

2. コード

[1] 幅優先検索:分析後、質問では、ある遺伝子配列をA別の遺伝子配列に変更する必要がありB、次の条件を満たす必要があることがわかります:
1.A配列B間に文字の違いが 1 つだけある;
2 . 変更された文字は、 from ACGTselect fromのみです。
3. 変換されたシーケンスは、Bstring 配列内に存在する必要がありますbank

上記の変換ルールに従って、すべての合法的な遺伝的変更を試して、最小数の変換を見つけることができます。手順は次のとおりです:
1.startと がend等しい場合は、この時点で直接戻ります0; 最終的な遺伝子配列がbank含まれていない場合は、質問の要件に従って、この時点では生成できず、直接戻ります−1;
2.まず、上記の変換ルールに従って、変換された可能性のある遺伝子 sss をキューから削除し、すべての可能な変更された遺伝子 (1 つなど) を試し、遺伝子の 1 つの文字を順番にAACCGGTA変更しs、すべての可能な遺伝子変更シーケンスを試しますs0s1s2si、 、s231 つの変更により、多くても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最大でも 1 つの要素があるため、時間計算量は です空間複雑度:、ここで、は遺伝子配列の長さ、は配列の長さです。正当なハッシュ テーブルには合計の要素がありキューには最大 1 つの要素があり、各要素のスペースは です。キューには最大1 つの要素があり、各要素のスペースは です。空間の複雑さは ですmO(C×n×m)
O(n×m)nmbankmmO(n)mO(n)O(n×m)

[2] 前処理の最適化:A分析後、質問では、ある遺伝子配列を別の遺伝子配列に変更する必要がありB、次の条件を満たす必要があることがわかります:
1.A配列Bと異なる文字が 1 つだけある;
2.変更された文字はAC、 、Gからのみ選択できますT
3. 変換されたシーケンスは、B文字列配列内に存在する必要がありますbank

方法 1 の幅優先検索方法として知られるこの方法では、bank合法的な遺伝的変化のみを前処理して検索できます。質問で指定された遺伝子ライブラリの長さはbank短いため、bank方法 1 のように毎回遺伝子の変更を計算する必要がなく、直接前処理して遺伝子ライブラリ内の各遺伝子の正当な変換を見つけることができます。各遺伝子の法的変化関係を隣接リストに保存し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キューには最大でも 1 つの要素があり、必要な時間は であるO(n)ため、時間計算量は ですO(m×n2)
空間計算量: O(n2)、ここで、nは配列の長さですbank合法的な遺伝的変化を計算するadjために必要な空間は でありO(n^2)、キューには最大でも 1 つの要素があるためn、空間複雑度は ですO(n^2)

おすすめ

転載: blog.csdn.net/zhengzhaoyang122/article/details/135162034