[leetcode] 1503: Previous Permutation With One Swap
思路
1. 找到一个交换数A
如果input各个数位上的值是按数位递增的,可以判断不能交换出数值更小的permutation。
-> 反过来也就是说,如果input从右到左数位上的值全部是递减的就没有answer。
-> 说明如果不满足这个条件就说明可以进行置换,从右到左找到第一个违反递减顺序的数A,需要用A与某一个数位交换。
可以证明:1) A右边的数组是降序的,内部交换不能得到更小的值;2) A左边的数值权重更大,用A左边的数去跟某个数位交换得到的smaller value一定会比用A交换更小。
所以用A交换才能得到比input原数值更小的、数值最大的permutation。(又是更小又是最大什么鬼好难表达
2. 找到另一个交换数B
另一个交换数B一定是在A的右边、数值比A小的数。
因为:1) 如果A与左边的某个数C交换,要得到比input原值更小的数那么C一定比A大,在左边数位上降低数值影响的权重一定比在靠右数位上的大。
2) A是第一个违反从右到左降序规律的元素,A右边一定存在比A数值更小的数。
又因为A右边是从右到左降序排列的,B应该是其中比A小的数中最靠右的。但还有一点要考虑,A右边并不是严格降序排列的,可能存在与B相邻且与B值相等的数D,这种情况下应选择这组相邻相等的数中最靠左的。因为B和D一定比A小,要交换A和其中一个数获得最大的小值数(什么鬼表达)应该要让A和其中最靠左的数交换,把大数值的A放在权重尽量高的位置上。
可以进一步推定B是在A右边、数值比A小的数中最靠右、如果有与其相等的相邻数则选其中最靠左。(什么鬼真的好难表达
总结:从右到左找到首个违反降序规律的数A,找到A数值比A小的数中“最靠右、相等相邻数中最靠左”的数B,交换AB。如果找不到A说明没有可交换数位。
不会用数学语言证明我真的越来越垃圾了
code
class Solution { public: vector<int> prevPermOpt1(vector<int>& A) { int N=A.size(), left; for (left=N-1;left>0;left--) { if (A[left-1]>A[left]) break; } if (left==0) return A; --left; int right=N-1; while (A[right]>=A[left]) --right; while (A[right]==A[right-1]) --right; swap(A[left],A[right]); return A; } };