题目:
请你设计一个迭代器类,包括以下内容:
一个构造函数,输入参数包括:一个 有序且字符唯一 的字符串 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;
}
};