出典:http://leonard1853.iteye.com/blog/1450085
1. next_permutationに遭遇しました(順列:シーケンスの意味)
今日、TC(SRM531-Division Two-Level One)で簡単な質問に遭遇しました。これは、昇順で配置されていない特定の配列の最小の辞書シーケンスを見つけることです(Aに含まれる場合、番号Aのシーケンスは辞書式順序でBよりも小さくなります)それらが異なる最初の位置の小さい数)。
解決策は非常に簡単です。つまり、配列を並べ替え(昇順)、最後から最初に交換可能な最初の位置を見つけます(番号が重複している可能性があるため)。
最後に、他の人の解決策を見てください。並べ替えた後、STLで関数next_permutaionを使用して、昇順ではない最初のシーケンスを直接見つけます。
2.next_permutationの実装原理
この関数は「STLソースコード分析」で見つかりました。原理の簡単な説明は次のとおりです。
STLには、next_permutationに加えて、関数prev_permutationもあります。これらは両方とも、順列と組み合わせを計算するために使用されます。前者は次の順列と組み合わせを見つけることであり、後者は前の順列と組み合わせを見つけることです。いわゆる「次へ」と「前へ」の本は、簡単な例を示しています。シーケンス{a、b、c}の場合、辞書シーケンスによれば、各要素は後者よりも小さく、aを修正した後、aはbcより大きいすべてが小さく、cがbより大きい、次のシーケンスが{a、c、b}、前のシーケンスの{a、c、b}が{a、b、c}である場合、同じことが推測できます。すべての6つのシーケンスは次のとおりです:{a、b、c}、{a、c、b}、{b、a、c}、{b、c、a}、{c、a、b}、{c、b 、a}、ここで、{a、b、c}には前の要素がなく、{c、b、a}には次の要素がありません。
next_permutationの関数プロトタイプは次のとおりです。
template<class BidirectionalIterator>
bool next_permutation(
BidirectionalIterator _First,
BidirectionalIterator _Last
);
template<class BidirectionalIterator, class BinaryPredicate>
bool next_permutation(
BidirectionalIterator _First,
BidirectionalIterator _Last,
BinaryPredicate _Comp
);
2番目のオーバーロードされた関数の3番目のパラメーターの場合、デフォルトの比較順序は未満です。次のシーケンスが見つかった場合はtrueを返し、それ以外の場合はfalseを返します。
関数実装の原則は次のとおりです。
現在のシーケンスでは、テールからフロントまで2つの隣接する要素を探します。前者はiとしてマークされ、後者はiiとしてマークされ、i < iiとしてマークされます。次に、最後から別の要素jを探します。i<* jが満たされると、i番目の要素とj番目の要素が交換され、i番目の要素の後のすべての要素(iiを含む)が上下逆に並べ替えられます。次のシーケンスがアップしています。
コードは次のように実装されています。
template<class BidirectionalIterator>
bool next_permutation(
BidirectionalIterator first,
BidirectionalIterator last
)
{
if(first == last)
return false; //空序列
BidirectionalIterator i = first;
++i;
if(i == last)
return false; //一个元素,没有下一个序列了
i = last;
--i;
for(;;) {
BidirectionalIterator ii = i;
--i;
if(*i < *ii) {
BidirectionalIterator j = lase;
while(!(*i < *--j));
iter_swap(i, j);
reverse(ii, last);
return true;
}
if(i == first) {
reverse(first, last); //全逆向,即为最小字典序列,如cba变为abc
return false;
}
}
}
prev_permutationは実装に似ています。つまり、逆引き参照です。
3.next_permutationを使用します
質問を考えてみましょう。シーケンス{a、d、c、e、b}の次のシーケンスは何ですか?前の分析を使用して答えを導き出し、コードで検証してください。
ここでは、配列とベクトルを使用してシーケンスを表し、next_permutationを使用して次のシーケンスを取得します(コンパイル環境:Dev-C ++):
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void TestArray()
{
char chs[] = {
'a', 'd', 'c', 'e', 'b'};
int count = sizeof(chs)/sizeof(char);
next_permutation(chs+0, chs + count);
printf("TestArray:\n");
for(int i = 0; i < count; i++) {
printf("%c\t", chs[i]);
}
printf("\n");
}
void TestVector()
{
char chs[] = {
'a', 'd', 'c', 'e', 'b'};
int count = sizeof(chs)/sizeof(char);
vector<char> vChs(chs, chs + count);
next_permutation(vChs.begin(), vChs.end());
printf("TestVector:\n");
vector<char>::iterator itr;
for(itr = vChs.begin(); itr != vChs.end(); itr++) {
printf("%c\t", *itr);
}
printf("\n");
}
int main(int argc, char *argv[])
{
TestArray();
printf("\n");
TestVector();
system("PAUSE");
return EXIT_SUCCESS;
}
4.まとめ
next_permutationとprev_permutationを使用して順列と組み合わせを見つけるのは非常に便利ですが、ヘッダーファイル#includeをインクルードすることを忘れないでください。
最後の順列には次の順列がありませんが、next_permutationを使用するとfalseが返されますが、このメソッドを使用すると、シーケンスは辞書シーケンスの最初になります。たとえば、cbaはabcになります。同じことがprev_permutationにも当てはまります。