1. 程式人生 > >[LeetCode] Sequence Reconstruction 序列重建

[LeetCode] Sequence Reconstruction 序列重建

Check whether the original sequence org can be uniquely reconstructed from the sequences in seqs. The org sequence is a permutation of the integers from 1 to n, with 1 ≤ n ≤ 104. Reconstruction means building a shortest common supersequence of the sequences in seqs (i.e., a shortest sequence so that all sequences in seqs

are subsequences of it). Determine whether there is only one sequence that can be reconstructed from seqs and it is the org sequence.

Example 1:

Input:
org: [1,2,3], seqs: [[1,2],[1,3]]

Output:
false

Explanation:
[1,2,3] is not the only one sequence that can be reconstructed, because [1,3,2] is also a valid sequence that can be reconstructed.

Example 2:

Input:
org: [1,2,3], seqs: [[1,2]]

Output:
false

Explanation:
The reconstructed sequence can only be [1,2].

Example 3:

Input:
org: [1,2,3], seqs: [[1,2],[1,3],[2,3]]

Output:
true

Explanation:
The sequences [1,2], [1,3], and [2,3] can uniquely reconstruct the original sequence [1,2,3].

Example 4:

Input:
org: [4,1,5,2,6,3], seqs: [[5,2,6,3],[4,1,5,2]]

Output:
true

這道題給了我們一個序列org,又給我們了一些子序列seqs,問這些子序列能否唯一的重建出原序列。能唯一重建的意思就是任意兩個數字的順序必須是一致的,不能說在一個子序列中1在4的後面,但是在另一個子序列中1在4的前面,這樣就不是唯一的了。還有一點就是,子序列seqs中不能出現其他的數字,就是說必須都是原序列中的數字。那麼我們可以用了一個一維陣列pos來記錄org中每個數字對應的位置,然後用一個flags數字來標記當前數字和其前面一個數字是否和org中的順序一致,用cnt來標記還需要驗證順序的數字的個數,初始化cnt為n-1,因為n個數字只需要驗證n-1對順序即可,然後我們先遍歷一遍org,將每個數字的位置資訊存入pos中,然後再遍歷子序列中的每一個數字,還是要先判斷數字是否越界,然後我們取出當前數字cur,和其前一位置上的數字pre,如果在org中,pre在cur之後,那麼直接返回false。否則我們看如果cur的順序沒被驗證過,而且pre是在cur的前一個,那麼標記cur已驗證,且cnt自減1,最後如果cnt為0了,說明所有順序被成功驗證了,參見程式碼如下:

解法一:

class Solution {
public:
    bool sequenceReconstruction(vector<int>& org, vector<vector<int>>& seqs) {
        if (seqs.empty()) return false;
        int n = org.size(), cnt = n - 1;
        vector<int> pos(n + 1, 0), flags(n + 1, 0);
        bool existed = false;
        for (int i = 0; i < n; ++i) pos[org[i]] = i;
        for (auto& seq : seqs) {
            for (int i = 0; i < seq.size(); ++i) {
                existed = true;
                if (seq[i] <= 0 || seq[i] > n) return false;
                if (i == 0) continue;
                int pre = seq[i - 1], cur = seq[i];
                if (pos[pre] >= pos[cur]) return false;
                if (flags[cur] == 0 && pos[pre] + 1 == pos[cur]) {
                    flags[cur] = 1; --cnt;
                }
            }
        }
        return cnt == 0 && existed;
    }
};

下面這種方法跟上面的方法大同小異,用兩個雜湊表來代替了上面的陣列和變數,其中m為數字和其位置之間的對映,pre為當前數字和其前一個位置的數字在org中的位置之間的對映。跟上面的方法的不同點在於,當遍歷到某一個數字的時候,我們看當前數字是否在pre中有對映,如果沒有的話,我們建立該對映,注意如果是第一個位置的數字的話,其前面數字設為-1。如果該對映存在的話,我們對比前一位數字在org中的位置和當前的對映值的大小,取其中較大值。最後我們遍歷一遍org,看每個數字的對映值是否是前一個數字的位置,如果有不是的返回false,全部驗證成功返回true,參見程式碼如下:

解法二:

class Solution {
public:
    bool sequenceReconstruction(vector<int>& org, vector<vector<int>>& seqs) {
        unordered_map<int, int> m, pre;
        for (int i = 0; i < org.size(); ++i) m[org[i]] = i;
        for (auto& seq : seqs) {
            for (int i = 0; i < seq.size(); ++i) {
                if (!m.count(seq[i])) return false;
                if (i > 0 && m[seq[i - 1]] >= m[seq[i]]) return false;
                if (!pre.count(seq[i])) {
                    pre[seq[i]] = (i > 0) ? m[seq[i - 1]] : -1;
                } else {
                    pre[seq[i]] = max(pre[seq[i]], (i > 0) ? m[seq[i - 1]] : -1);
                }
            }
        }
        for (int i = 0; i < org.size(); ++i) {
            if (pre[org[i]] != i - 1) return false;
        }
        return true;
    }
};

參考資料: