[バックトラッキング] [leetcode]文字の組み合わせイテレータ

トピック:

以下を含むイテレータクラスを設計してください。

コンストラクターである入力パラメーターには、順序付けられた一意の文字列文字(文字列には小文字の英字のみが含まれます)と数値combinationLengthが含まれます。
関数next()は、辞書式順序で長さがcombinationLengthである次の文字の組み合わせを返します。
関数hasNext()は、combinationLengthの長さの次の文字の組み合わせがある場合にのみTrueを返します。それ以外の場合は、Falseを返します。
 

例:

CombinedIterator iterator = newCombinationIterator( "abc"、2); //イテレータを作成します

iterator.next(); // "ab"を返す
iterator.hasNext(); // trueを返す
iterator.next(); // "ac"を返す
iterator.hasNext(); // true
iterator.next();を返す// "bc"を返し
ますiterator.hasNext(); // falseを返します
 

促す:

  • 1 <=組み合わせの長さ<=文字。長さ<= 15
  • テストデータの各グループには、最大10 ^ 4の関数呼び出しが含まれます。
  • タイトルは、関数nextが呼び出されるたびに次の文字の組み合わせが存在することを保証します。

ソース:

1286.モノグラムイテレータ

問題解決のアイデア1:バックトラック

質問とk数の組み合わせは同じですが、この質問のすべての組み合わせは一度に出力されるのではなく、1つずつ出力されるため、最初にバックトラッキングメソッドを使用してすべての組み合わせをキューに入れ、 nextを呼び出すときの行要素の先頭。

このアイデアは多くのスペースを占有しますが、プロンプト内の呼び出しの数には制限があるため、これを使用することをお勧めします。

  • 再帰的終了条件:パスのサイズが長さの要件を満たしている
  • 剪定条件:結果が10,000を超える場合||残りの要素が不十分な場合

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-ary

文字セットの長さはNで、各文字には下付き文字があります。下付き文字はN基数を形成します。Nを入力すると、1が追加されます。1を入力した後の番号は0に変更できませんが、変更されることに注意してください。増分して連続します。

たとえば、文字セットabcdefgの長さは7で、出力の長さは3です。最初の出力結果は、添え字0、1、2に対応する文字abcである必要があり、その後、添え字が累積され、添え字の終わりが7に達すると、前方に移動します。

2番目の数字は1から2まで運ばれます。3番目の数字は0ではなく2+ 1です。つまり、数字から始まるすべての数字は増加する連続シーケンスである必要があります。ここでは2、3です。下の図の青い部門は、キャリー後の次の数字のセットです。

配列パスを定義し、出力文字の添え字を記録し、最初に[0,1,2、...、combinationLength-1]、パスの最後の要素を指すポインターtopを定義し、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;
    }
};

 

おすすめ

転載: blog.csdn.net/hbuxiaoshe/article/details/114937858