- 题目描述
写出一个算法,实现如下功能:
给定一个N位数字组成的数,找出比这个数大的由相同数字组成的下一个数
例如:如果数字为 25468, 则结果为25486 如果数字为 21765, 则结果为 25167 如果数字为 54321, 则结果为 54321 (因为没有比这个数大的相同数字组成的值)
2.问题分析
- 因为N是一个没有范围的数,因此在解决此题时排出用N-位整数作为输入,而是用字符串来处理
- 若存在有连续数字是相同的应该如何处理?
- 观察例子可以得到如下思路
一个N数如83557761,从右往左,找到当前位置上的数比它左边的数大为止(相等也要继续),即找到左边第一个7,比5大,得到7761,逆序为1677,然后从左往右找到第一个比5大的数6,交换5和6,即得到83561577。
3.此算法一般性讲解
把N位数分为两部分前i位和后N-i位,其中后N-i位从右往左是不减的(要么递增,要么相等),因此,不管怎样调这N-i,数都不会增大。按前面的分法,第i位一定比第i+1位小,也可能比第i+2位小,以此类推,找到第i+j位数,它比第i位数大,但是第i+j+1位数比第i位数小,交换第i位和第i+j位,然后提取i+1到第i+j-1这部分数字和第i+j+1到第N位的数字,这两部分分别逆序之后交换位置放置即可。结合例子可以理解。
4.代码和注释
1 #include<iostream> 2 #include<string> 3 #include <algorithm> 4 using namespace std; 5 6 7 int main() 8 { 9 string digit; 10 cin >> digit; 11 12 int len,i,j; 13 len = digit.length(); 14 //N<=1直接输出 15 if(len <= 1) 16 { 17 cout << digit<<endl; 18 return 0; 19 } 20 //当digit中有连续的相同数字,在从右到左遍历时仍然继续遍历 21 for(i=len-1;i>=1;i--) 22 if(digit[i]>digit[i-1])break; 23 24 //digit从右到左都是不减的,则不用调整 25 if(i==0) 26 { 27 cout << digit<<endl; 28 return 0; 29 } 30 //提取前i位 31 string pre = digit.substr(0,i); 32 //提取后N-i位 33 string end = digit.substr(i,len-i); 34 35 reverse(end.begin(), end.end()); //逆序 36 37 38 int index = pre.length(); 39 int num =pre[index-1]; 40 for(j=0;j<end.length();j++) 41 if(end[j]>=num)break; 42 43 //交换第i位和第i+j位 44 pre[index-1] = end[j]; 45 end[j]=num; 46 47 digit = pre+end;//合并即可 48 49 cout<<digit<<endl; 50 return 0; 51 }