【回溯】【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時都存在下一個字母組合。
來源:
解題思路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;
}
};