【模板】康拓展开与康拓逆展开

本文深入探讨了康拓展开和逆展开算法,通过实例讲解如何计算字符串在字典序中的位置,以及如何根据位置反推出对应的字符串。适用于竞赛编程中涉及排列组合的问题。

康拓展开与逆展开链接:https://www.cnblogs.com/Howe-Young/p/4348777.html(标准模板)

康拓展开:比当前位置小的数的个数*阶乘 

习题:http://nyoj.top/problem/139


#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <cstdio>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const ll maxn = 1e5+100;
const ll mod = 998244353;
const ld pi = acos(-1.0);
const ll inf = 1e18;

ll n,fac[15];
bool flag[15];

void getnum()
{
    fac[0] = 1;
	fac[1] = 1;
	for(ll i = 2; i <= 15; i++)
	{
		fac[i] = fac[i-1]*i;
	}
	return ;
}

int main()
{
	ios::sync_with_stdio(false);

	getnum();	// 获取阶乘结果 
	
	cin >> n;
	
	while(n--)
	{
		
		memset(flag,0,sizeof(flag));
		string s;
		cin >> s;
		
		ll ans = 0;
		
		for(ll i = 0; i < s.size()-1; i++)
		{
			ll num = 0;
			flag[ s[i]-'a' ] = 1;  //标记出现过 
			
			for(ll j = 0; j < s[i]-'a'; j++)  //枚举比s[i]小的字符 
			{
				if(flag[j] == 0)
					num++;    
			}
			
			ans += num*fac[ 12 - i - 1 ];  //个数*阶乘 
			
		}	
		cout << ans+1 << endl;	
	}
	
	return 0;
}

康拓逆展开:


习题:http://nyoj.top/problem/143

 注意阶乘数组 fac[0] = 1


#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <cstdio>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const ll maxn = 1e5+100;
const ll mod = 998244353;
const ld pi = acos(-1.0);
const ll inf = 1e18;

ll n,fac[15],T;
bool flag[15];

void getnum()
{
	fac[0] = 1;
	fac[1] = 1;
	for(ll i = 2; i <= 15; i++)
	{
		fac[i] = fac[i-1]*i;
	}
	return ;
}

int main()
{
	ios::sync_with_stdio(false);

	getnum();	// 获取阶乘结果 
	
	cin >> T;
	
	while(T--)
	{
		memset(flag,0,sizeof(flag));
		
		cin >> n;
		n--;
		
		ll len = 12;  //字符串长度 
		
		string ans;
		
		for(ll i = 1; i <= len; i++)
		{
			
			ll t = n/fac[len - i]; //代表有t个数字比当前数字小 
			n = n%fac[len-i];
			
			ll temp = 0,num = 0;  //temp表示用过的数,num表示没用过的 
			for(ll j = 0; j < len; j++)
			{
				if(num > t)  //找到t个没用过的数字就可以停止了 
					break;
					
				if(flag[j] == 1) // 用过的 
				{
					temp++;
				}
				else
				{
					num++;
				}
			}
			
			flag[temp+num-1] = 1;
			ans += char('a'+ (temp+num-1) );
			//cout << ans << endl;
		}
		
		cout << ans << endl;
			
	}
	return 0;
}

 

展开是根据给定的字典序位置,推算出具体的排列。以下为两种不同的展开C++代码实现: ### 实现一 ```cpp #include <iostream> #include <vector> using namespace std; // 计算阶乘 int factorial(int n) { int result = 1; for (int i = 2; i <= n; ++i) result *= i; return result; } // 展开:根据给定的字典序位置,推算出具体的排列 vector<int> inverseCantor(int n, int k) { vector<int> perm; // 存储结果排列 vector<int> nums; // 可供选择的数字 vector<bool> used(n + 1, false); // 标记数字是否已使用 // 初始化数字列表 for (int i = 1; i <= n; ++i) { nums.push_back(i); } // 展开是 1-based,需要将 k 减去 1 转为 0-based k--; // 逐位计算每一位的数字 for (int i = 0; i < n; ++i) { int fact = factorial(n - i - 1); // (n - i - 1)! int idx = k / fact; // 计算当前位应该选择哪个数字 perm.push_back(nums[idx]); // 添加数字到排列中 nums.erase(nums.begin() + idx); // 从数字列表中移除该数字 k = k % fact; // 更新 k } return perm; } int main() { int n = 3, k = 4; // 求第 4 个排列 vector<int> result = inverseCantor(n, k); cout << "第 " << k << " 个排列是: "; for (int num : result) { cout << num << " "; } cout << endl; return 0; } ``` 此代码中,`factorial` 函数用于计算阶乘,`inverseCantor` 函数根据给定的字典序位置 `k` 和排列长度 `n` 进行展开,最终在 `main` 函数中进行测试并输出结果 [^2]。 ### 实现二 ```cpp #include<iostream> using namespace std; int n,k,a[25],vis[25],mod; long long jc[25]={1}; int main(){ cin>>n>>k; for(int i=1;i<n;i++) jc[i]=jc[i-1]*i; mod=k-1; for(int i=1;i<=n;i++){ k=mod/jc[n-i]; mod=mod%jc[n-i]; for(int j=1;j<=n;j++) if(!vis[j]){ if(!k){ vis[j]=1; cout<<j; break; } k--; } } return 0; } ``` 此代码过输入排列长度 `n` 和字典序位置 `k`,进行展开并输出具体排列 [^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值