1. 程式人生 > 其它 >【回溯】【leetcode】字母組合迭代器

【回溯】【leetcode】字母組合迭代器

題目:

請你設計一個迭代器類,包括以下內容:

一個建構函式,輸入引數包括:一個有序且字元唯一的字串characters(該字串只包含小寫英文字母)和一個數字combinationLength。
函式next(),按字典序返回長度為combinationLength 的下一個字母組合。
函式hasNext(),只有存在長度為combinationLength 的下一個字母組合時,才返回True;否則,返回 False。

示例:

CombinationIterator iterator = new CombinationIterator("abc", 2); // 建立迭代器 iterator

iterator.next(); // 返回 "ab"
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 "ac"
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 "bc"
iterator.hasNext(); // 返回 false

提示:

  • 1 <= combinationLength <=characters.length <= 15
  • 每組測試資料最多包含10^4次函式呼叫。
  • 題目保證每次呼叫函式next時都存在下一個字母組合。

來源:

1286. 字母組合迭代器

解題思路1:回溯

題目與 k個數的組合 是一樣的,但是本題的所有組合不是一次性輸出而是一個一個的輸出,所以先用回溯的方法將所有組合放入一個佇列中,呼叫next時返回隊頭元素即可。

本思路佔用空間較大,不過提示中有呼叫次數限制,我們不妨利用一下。

  • 遞迴終止條件:path大小滿足長度需求
  • 剪枝條件:當結果超過1萬時 || 剩餘元素不足時

class CombinationIterator {
public:
    queue<string> result;
    string path;
    CombinationIterator(string characters, int combinationLength) {
        back(characters, 0, combinationLength);
    }
    
    string next() {
        string r = result.front();
        result.pop();
        return r;
    }
    
    bool hasNext() {
        return !result.empty();
    }

    void back(const string& characters, int start, int len) {
        if (path.size() == len) {
            result.push(path);
            return;
        }
        for (int i = start; i < characters.size(); i++) {
            if (result.size() > 10000 || characters.size() - i + path.size() + 1 < len) break; // 剪枝
            path.push_back(characters[i]);
            back(characters, i+1, len);
            path.resize(path.size() - 1);
        }
    }
};

解題思路2:N進位制

字符集的長度為N,每個字元都有一個下標,下標組成一個N進位制數,逢N進1,注意進1後其後面的數字不能改為0,而是改為遞增且連續。

例如,字符集abcdefg長度為7,輸出長度為3。第一個輸出的結果必然是下標0,1,2對應的字元abc,然後累加下標,當下標末尾達到7時向前進位。

第2個數字由1進位成2,第3個數字不能是0而是2+1,即從進位數字開始的所有數字應該是一個遞增連續序列,這裡是2,3。下圖藍色部門都是進位後的下一組數字。

定義一個數組path,記錄輸出的字元的下標,初始[0,1,2,...,combinationLength-1],定義一個指標top指向path最後一個元素,累加path[top]。

path[top]達到最大長度N時,累加path[top-1],同時判斷path[top-1]的長度是否達到N-1,如果達到N-1,累加path[top-2],並判斷其長度是否達到N-2,。。。,依次迴圈判斷直到結束。

top到達-1時,所有的組合已完成。

class CombinationIterator {
public:
    vector<int> path;
    string input;
    int len;
    int top;
    CombinationIterator(string characters, int combinationLength) {
        input = characters;
        len = combinationLength;
        for (int i = 0; i < len; i++) {
            path.push_back(i);
        }
        top = len - 1;
    }
    
    string next() {
        string result(len, 'a');
        for (int i = 0; i < len; i++) {
            result[i] = input[path[i]];
        }
        add();
        return result;
    }

    void add() {
        top = len - 1;
        path[top]++;
        int max = input.size();
        while (top >= 0 && path[top] == max) { // 進位後滿則一直進位
            top--;
            max--;
            if (top < 0) return;
            path[top]++;
        }
        // 調整以top開頭的序列連續遞增
        for (int i = top + 1; i < len; i++) {
            path[i] = path[i-1] + 1;
        }
    }
    
    bool hasNext() {
        return top >= 0;
    }
};