转载自:https://www.cnblogs.com/luruiyuan/p/5914909.html

目标

STL中的next_permutation 函数和 prev_permutation 两个函数提供了对于一个特定排列P,求出其后一个排列P+1和前一个排列P-1的功能。

这里我们以next_permutation 为例分析STL中实现的原理,prev_permutation 的原理与之类似,我们在最后给出它们实现上差异的比较

问题:

给定一个排列P,求出其后一个排列P+1是什么。

思路

按照字典序的定义不难推出,正序,是一组排列中最小的排列,而逆序,则是一组排列中最大的排列。

从字典序的定义出发,我们可以看到,n个元素的排列全排列中,从给定排列P 求解下一排列P+1 ,假设两个排列有前k位是相同的(0<=k<=n),那么我们只需要在后面n-k个元素的排列 P(n-k)中求得下一个排列即可

既然我们关心的是后面 n-k位的排列,那么不妨开开脑洞,从后向前考察排列。

首先我们举一个比较极端的例子:排列 1 2 3 4 5

很显然,这是一个正序排列(递增序列),因此这是这几个数字所组成的排列中最小的排列,记为P1.

现在我们要求出P2,P2是 1 2 3 5 4. 我们可以看到,P2的前三位和P1的前三位的排列完全相同,唯一的变化是最后两位颠倒顺序,这一顺序的颠倒有何玄机呢?——使得最后两位从正序的 4 5 变成了逆序的 5 4.

接着求P3.P3是 1 2 4 3 5. 我们看到,最后两位已经是逆序,不可能有字典序更大的排列,因此必须考虑更多的位,在后3个元素中,3 5 4 显然不是逆序,因此一定存在字典序更大的排列方式,我们由此确定了n-k==3

我们现在要在 3 5 4 中求得下一个排列,3 5 4 不是一个逆序,是因为 3 后面有元素大于3 。我们要在大于3的数字中选择最小的那个,和3交换,因为这样可以保证得到最小的首位元素。对于这个例子,我们选择将3和4进行交换,而不是3 和 5,这样得到的首位元素是4. 现在我们得到了排列 4 5 3 。

显然,4 5 3 并不是我们想要的下一个排列,下一个排列是 4 3 5. 观察区别,不难看出,首位元素一定是4,但是5 3 这个子排列是一个逆序排列

为什么会是逆序排列?

因为我们寻找的时候就以是不是逆序为分割点,3 恰好是第一个非逆序的元素,而4作为与3 交换的元素,又比3要大,因此交换后得到的 5 3 一定是逆序的排列。

逆序排列没有下一排列,但是将逆序排列反向后,我们就得到了对应的正序排列,而正序排列是当前元素所能形成的最小排列,因此,4 3 5 是4 为首位元素所能形成最小排列,而前3 位没有变化,故我们得到了下一排列P3.

另外,大于3的最小元素,即4 ,也是第一个大于3的元素,因为 5 4 是个逆序排列。

更一般地,例如对于可重集排列 1 2 3 7 6 5 2 1

我们首先寻找第一个非逆序元素,这里是3,然后从后向前寻找第一个大于3的元素,这里是5,交换,得到 5 7 6 3 2 1 的子排列,然后反向,即可得到下一排列。如果没有找到第一个非逆序元素,那么说明该排列已经是最大排列。

我的理解是:既然要找下一个排列的化,我们就要从后面找到最大的逆序子排列,然后从中找到最小的大于第一个非逆序元素的元素,然后就交换他们的值,这就保证了它在大于原序列的排列中是最接近的,因为逆序排列是一个排列中字典序最大的

那个,所以我们为了使它和原排列最接近,我们要把它转换成升序排列,因为升序排列是一个排列中字典序最小的,这就保证了得到的排列是原排列的下一个排列。

例:如果我们要求 1 6 3 4 7 6 3 1的下一个排列

我们先从后面找到最长的逆序排列:7 6 3 1

因为7 6 3 1这个子排列是最大的排列,所以我们不能找到比它更大的排列(对于这个长度为4的子排列)

所以我们要通过更改这个逆序列前面的一个数字来使它变,因为要找到它的下一个排列,所以我们要找的是比原排列更大的最小的那个

我们就从逆序列中找到最小的比第一个非逆序数列数字大的数字,然后交换他们的值,交换后的逆序列任然是逆序的(因为我们找的是最小的比第一个非逆序数列数字大的数字,交换后它还是小于它前面的数字,大于它后面的数字)

又因为逆序排列是最大的,我们要找最小的,只要把它翻转过来就可以了,然后和前面的连接在一起就得到了下一个排列

代码:

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
string nextpermutation(string a){
int i,ii,j;
if(a.size()==1)
return a;
i=a.size()-1;
ii=a.size()-2;
while(ii>=0&&(a[ii]>=a[i])){//从后面找到最长的逆序子排列
ii--;
i--;
}
int k=-1;
if(ii>=0){
for(j=a.size()-1;j>ii&&k<0;j--){//从逆序排列中找最小的大于第一个非逆序排列数字的数字
if(a[ii]<a[j]){
k=j;
}
}
swap(a[k],a[ii]);
}
reverse(a.begin()+ii+1,a.end());
return a;
}
int main(){
string s,a,b;
cin>>s;
for(a=s;b!=s;a=b){
cout<<a<<endl;
b=nextpermutation(a);
}
return 0;
}

  

STL next_permutation 算法原理和实现的更多相关文章

  1. STL next_permutation 算法原理和自行实现

    目标 STL中的next_permutation 函数和 prev_permutation 两个函数提供了对于一个特定排列P,求出其后一个排列P+1和前一个排列P-1的功能. 这里我们以next_pe ...

  2. 【STL】next_permutation的原理和使用

    1.碰到next_permutation(permutation:序列的意思) 今天在TC上碰到一道简单题(SRM531 - Division Two - Level One),是求给定数组不按升序排 ...

  3. C++复习:STL之算法

    算法 1算法基础 1.1算法概述 算法部分主要由头文件<algorithm>,<numeric>和<functional>组成. <algorithm> ...

  4. STL所有算法简介 (转) http://www.cnblogs.com/yuehui/archive/2012/06/19/2554300.html

    STL所有算法简介 STL中的所有算法(70个) 参考自:http://www.cppblog.com/mzty/archive/2007/03/14/19819.htmlhttp://hi.baid ...

  5. 初探STL之算法

    算法 STL算法部分主要由头文件<algorithm>,<numeric>,<functional>组成.要使用 STL中的算法函数必须包括头文件<algor ...

  6. [C++ STL] 常用算法总结

    1 概述 STL算法部分主要由头文件<algorithm>,<numeric>,<functional>组成.要使用 STL中的算法函数必须包含头文件<alg ...

  7. 打印全排列和stl::next_permutation

    打印全排列是个有点挑战的编程问题.STL提供了stl::next_permutation完美的攻克了这个问题. 可是,假设不看stl::next_permutation,尝试自己解决,怎么做? 非常自 ...

  8. 从时序异常检测(Time series anomaly detection algorithm)算法原理讨论到时序异常检测应用的思考

    1. 主要观点总结 0x1:什么场景下应用时序算法有效 历史数据可以被用来预测未来数据,对于一些周期性或者趋势性较强的时间序列领域问题,时序分解和时序预测算法可以发挥较好的作用,例如: 四季与天气的关 ...

  9. Bagging与随机森林算法原理小结

    在集成学习原理小结中,我们讲到了集成学习有两个流派,一个是boosting派系,它的特点是各个弱学习器之间有依赖关系.另一种是bagging流派,它的特点是各个弱学习器之间没有依赖关系,可以并行拟合. ...

随机推荐

  1. Install rapyuta Robot Cloud Engine on Ubuntu14.04

    # -Rapyuta-installation-in-Ubuntu14.04-LTS-Trusty-This gzip folder is a tested version which can ins ...

  2. memcache 常用方法

    <?php $memcache = new Memcache; //initialised memcahe $memcache->connect("127.0.0.1" ...

  3. servlet injection analysis

    一. Spring不能通过注解向Servlet中注入实例的原理 想了解此问题的原理,就要了解tomcat启动后 servlet和spring的加载顺讯. 1.  tomcat启动后先加载web.xml ...

  4. bootloader研究最后一关(中)

    2011-03-12 17:04:13 今天的目的是要把bin文件烧录到nandflash中. 其实我有一个问题一直没弄明白.S3C2440自带的RAM和ROM是多大?看了规格书说它能控制1G的8个b ...

  5. 腾讯云服务器使用smtp发送邮件

    问题:在腾讯云服务器上使用自编写的邮件服务失败.查其原因,是该邮件服务调用smtpclient.Send(mailMessage)时,出现错误:由于连接方在一段时间后没有正确答复或连接的主机没有反应, ...

  6. HTTPS通信原理

      https的实现原理https用到了多种加密算法来实现通信安全,其中两种基本的加解密算法类型解释如下:(1)对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES ...

  7. oracle insert、append、parallel、随后查询的redo与磁盘读写

    SQL> set autotrace traceonly statistics; SQL> insert into big_table_dir_test1 select * from bi ...

  8. Python爬虫(四)——豆瓣数据模型训练与检测

    前文参考: Python爬虫(一)——豆瓣下图书信息 Python爬虫(二)——豆瓣图书决策树构建 Python爬虫(三)——对豆瓣图书各模块评论数与评分图形化分析 数据的构建 在这张表中我们可以发现 ...

  9. 通过WireShark抓取iPhone联网数据方法

    通过WireShark抓取iPhone联网数据方法 命令行 rvictl -s <UDID> 然后再wireshark选择rvi0进行抓包即可 抓包完后,移除用命令 rvictl -x & ...

  10. JS求任意字符串中出现最多的字符以及出现的次数

    我爱撸码,撸码使我感到快乐!大家好,我是Counter本节讲讲如何利用JS来查找任意给定的字符串,求字符串中出现次数最多的字符,出现的次数.直接上代码了,该注释的都注释啦.非常轻松加愉快.效果如下: ...