编写自定义 std::next_permutation(string) 函数
Writing a custom std::next_permutation(string) function
我正在尝试实现类似于 std::next_permutation(std::string w)
的功能。请参阅下面的代码,了解我是如何执行此操作的:
string biggerIsGreater(string w) {
// 1. Find the largest non-increasing suffix. This suffix already has the maximum permutation.
// 2. Assign the char before that as the pivot
// if there is no such char,
// then the whole string is non-increasing => no next permutation.
// 3. Swap the pivot with the smallest element in the suffix that is greater than the pivot itself.
// If there are multiple smallest char > pivot, then choose the rightmost one
// As this will keep the suffix non-increasing.
// 4. reverse the order of the suffix, so that it is now non-decreasing.
// This is the lowest next permutation.
// 1.
int suffix_start = (int)w.length()-1;
//single character has no next permutation:
if (suffix_start == 0) return "no answer";
// else keep decreasing suffix_start until the element is not > the previous.
while(w[suffix_start-1] <= w[suffix_start]) {
suffix_start--;
if(suffix_start==0) return "no answer";
}
// 2.
int pivot = suffix_start - 1;
// 3.
int smallest_char = (int)w.length()-1;
while(w[smallest_char] <= w[pivot]) smallest_char--;
if(w[smallest_char] == w[smallest_char-1]) while (w[smallest_char] != w[smallest_char-1]) smallest_char--;
swap(w[pivot], w[smallest_char]);
// 4.
reverse(w.begin() + pivot + 1, w.end());
return w;
}
但是,此代码似乎无法处理 bb
这样的字符串。请问你能告诉我哪里做错了吗?
如果我在反转后打印出w
,我得到这个:(第一行是测试用例的数量):
输入:
5
ab
bb
hefg
dhck
dkhc
然后程序打印 ba
(这意味着第一个有效)但没有打印其他内容。
所以错误是在处理bb
.
注意:我的 objective 是在 <algorithm>
.
没有 std::next_permutation()
功能的情况下写的
我re-implemented你的功能我有自己的方式,如果它不是一个可以接受的答案,那么至少它对教育目的是有益的。也许通过我的代码你可以找出你的错误所在。
如果这是最后一个排列,如 "bb"
的情况,则返回第一个字典排列,与 std::next_permutation().
相同
#include <algorithm>
#include <iostream>
#include <string>
std::string next_permutation(std::string x) {
if (x.size() <= 1)
return x;
std::ptrdiff_t i = 0, j = 0;
for (i = x.size() - 2; i >= 0 && x[i] >= x[i + 1]; --i);
if (i >= 0) {
for (j = i + 1; j < x.size() && x[i] < x[j]; ++j);
--j;
std::swap(x[i], x[j]);
}
std::reverse(x.begin() + (i + 1), x.end());
return x;
}
int main() {
auto Test = [](auto const & s){
std::cout << "'" << s << "' -> '"
<< next_permutation(s) << "'" << std::endl;
};
Test("ab");
Test("bb");
Test("hefg");
Test("dhck");
Test("dkhc");
Test("abc");
Test("aabb");
Test("cba");
}
输出:
'ab' -> 'ba'
'bb' -> 'bb'
'hefg' -> 'hegf'
'dhck' -> 'dhkc'
'dkhc' -> 'hcdk'
'abc' -> 'acb'
'aabb' -> 'abab'
'cba' -> 'abc'
这是@Arty 的解决方案。完全归功于他。
我添加了评论来尝试解释它是如何工作的,以便我能更好地理解它。
#include <string>
#include <iostream>
#include <algorithm>
std::string next_permutation(std::string x) {
std::ptrdiff_t i = 0, j = 0;
// start with penultimate element
// as long as i doesn't hit the start and the sequence is non-increasing, keep decreasing i.
// the value of i we reach is the first element from the right which is not in reverse order (=> the maximum permutation)
// this is the pivot
for (i = x.size() - 2; i >= 0 && x[i] >= x[i + 1]; --i);
// if the whole array is reverse order, there is no maximum permutation.
if (i < 0)
return {};
// then find the first element after i which is less than x[i].
for (j = i + 1; j < x.size() && x[i] < x[j]; ++j);
// stop at the next element -- I like this as it avoids the problem of acccb, if a is the pivot
// then this code will stop at the first c.
--j;
// swap the elements
std::swap(x[i], x[j]);
// reverse the remaining array in order to minimise it, as we know it is in descending order.
std::reverse(&x[i + 1], &x[x.size()]);
return x;
}
int main() {
auto Test = [](auto const& s) {
std::cout << "'" << s << "' -> '"
<< next_permutation(s) << "'" << std::endl;
};
Test("abc");
Test("bb");
Test("aabb");
Test("cba");
}
我正在尝试实现类似于 std::next_permutation(std::string w)
的功能。请参阅下面的代码,了解我是如何执行此操作的:
string biggerIsGreater(string w) {
// 1. Find the largest non-increasing suffix. This suffix already has the maximum permutation.
// 2. Assign the char before that as the pivot
// if there is no such char,
// then the whole string is non-increasing => no next permutation.
// 3. Swap the pivot with the smallest element in the suffix that is greater than the pivot itself.
// If there are multiple smallest char > pivot, then choose the rightmost one
// As this will keep the suffix non-increasing.
// 4. reverse the order of the suffix, so that it is now non-decreasing.
// This is the lowest next permutation.
// 1.
int suffix_start = (int)w.length()-1;
//single character has no next permutation:
if (suffix_start == 0) return "no answer";
// else keep decreasing suffix_start until the element is not > the previous.
while(w[suffix_start-1] <= w[suffix_start]) {
suffix_start--;
if(suffix_start==0) return "no answer";
}
// 2.
int pivot = suffix_start - 1;
// 3.
int smallest_char = (int)w.length()-1;
while(w[smallest_char] <= w[pivot]) smallest_char--;
if(w[smallest_char] == w[smallest_char-1]) while (w[smallest_char] != w[smallest_char-1]) smallest_char--;
swap(w[pivot], w[smallest_char]);
// 4.
reverse(w.begin() + pivot + 1, w.end());
return w;
}
但是,此代码似乎无法处理 bb
这样的字符串。请问你能告诉我哪里做错了吗?
如果我在反转后打印出w
,我得到这个:(第一行是测试用例的数量):
输入:
5
ab
bb
hefg
dhck
dkhc
然后程序打印 ba
(这意味着第一个有效)但没有打印其他内容。
所以错误是在处理bb
.
注意:我的 objective 是在 <algorithm>
.
std::next_permutation()
功能的情况下写的
我re-implemented你的功能我有自己的方式,如果它不是一个可以接受的答案,那么至少它对教育目的是有益的。也许通过我的代码你可以找出你的错误所在。
如果这是最后一个排列,如 "bb"
的情况,则返回第一个字典排列,与 std::next_permutation().
#include <algorithm>
#include <iostream>
#include <string>
std::string next_permutation(std::string x) {
if (x.size() <= 1)
return x;
std::ptrdiff_t i = 0, j = 0;
for (i = x.size() - 2; i >= 0 && x[i] >= x[i + 1]; --i);
if (i >= 0) {
for (j = i + 1; j < x.size() && x[i] < x[j]; ++j);
--j;
std::swap(x[i], x[j]);
}
std::reverse(x.begin() + (i + 1), x.end());
return x;
}
int main() {
auto Test = [](auto const & s){
std::cout << "'" << s << "' -> '"
<< next_permutation(s) << "'" << std::endl;
};
Test("ab");
Test("bb");
Test("hefg");
Test("dhck");
Test("dkhc");
Test("abc");
Test("aabb");
Test("cba");
}
输出:
'ab' -> 'ba'
'bb' -> 'bb'
'hefg' -> 'hegf'
'dhck' -> 'dhkc'
'dkhc' -> 'hcdk'
'abc' -> 'acb'
'aabb' -> 'abab'
'cba' -> 'abc'
这是@Arty 的解决方案。完全归功于他。
我添加了评论来尝试解释它是如何工作的,以便我能更好地理解它。
#include <string>
#include <iostream>
#include <algorithm>
std::string next_permutation(std::string x) {
std::ptrdiff_t i = 0, j = 0;
// start with penultimate element
// as long as i doesn't hit the start and the sequence is non-increasing, keep decreasing i.
// the value of i we reach is the first element from the right which is not in reverse order (=> the maximum permutation)
// this is the pivot
for (i = x.size() - 2; i >= 0 && x[i] >= x[i + 1]; --i);
// if the whole array is reverse order, there is no maximum permutation.
if (i < 0)
return {};
// then find the first element after i which is less than x[i].
for (j = i + 1; j < x.size() && x[i] < x[j]; ++j);
// stop at the next element -- I like this as it avoids the problem of acccb, if a is the pivot
// then this code will stop at the first c.
--j;
// swap the elements
std::swap(x[i], x[j]);
// reverse the remaining array in order to minimise it, as we know it is in descending order.
std::reverse(&x[i + 1], &x[x.size()]);
return x;
}
int main() {
auto Test = [](auto const& s) {
std::cout << "'" << s << "' -> '"
<< next_permutation(s) << "'" << std::endl;
};
Test("abc");
Test("bb");
Test("aabb");
Test("cba");
}