您的位置:首页 > 其它

康拓展开

2015-04-27 12:43 218 查看

康拓展开

康拓展开在求字典序问题上是一个十分强大的算法

首先,康拓展开是形如以下的式子

X=an∗(n−1)!+an−1∗(n−2)!+...+a1∗0!

下面介绍一下此式在康拓展开中的含义

首先我们假设有一个长为n的字符串s,下标从0开始

ai的值是在后i-1个字符中,其中比s[n-i]小的字符个数

那么在此字符串在所有的排列中,字典序比此字符串小的有X个

那么我们就可以快速解决一些字典序的问题

首先就是如上所说的求有多少个比这个字典序小的字符串数,代码如下

@Frosero
#include <iostream>
#include <cstring>

using namespace std;

long long mul(long long n){
if(n == 0 || n == 1) return n;
return n * mul(n - 1);
}

long long KT(string &s){  //康拓展开
long long ans = 0;
for(string::size_type i=0;i<s.size();i++){
long long ai = 0;
for(string::size_type j=i+1;j<s.size();j++){ //计算ai
if(s[i] > s[j]){
ai++;
}
}
ans += ai * mul(s.size() - i - 1);
}
return ans;  //若想求此字符串是第几小,则返回ans+1
}

int main(){
string s = "CAB";
cout << KT(s);
return 0;
}


康拓展开的逆用:快速找到第K个字典序的排列

方法:我们可以从高位到低位依次推断系数an,代码如下

@Frosero
#include <iostream>
#include <cstring>
#include <set>
#include <map>
#include <cstdlib>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define LL long long

using namespace std;

long long mul(long long n){
if(n == 0 || n == 1) return 1;
return n * mul(n - 1);
}

bool used[25];

void inv_KT(long long k,long long len,long long *a){
k--;
memset(used,false,sizeof(used));
int tot = 0;
for(int i=len-1;i>=0;i--){
int ai = k / mul(i);     //确定最高位的系数ai
int cnt = 0;
for(int j=1;;j++)if(!used[j]){ //只有未被使用的数字才加入计数cnt
if(cnt == ai){
a[tot++] = j;
used[j] = true;     //used[i]表示数字i是否在前面已经被使用
break;             //若已被使用则used[i]标记为true
}
cnt++;
}
k = k % mul(i);        //确定余数,继续进行后面的运算
}
}

int main(){
long long K = 5,len = 3,a[25];
inv_KT(K,len,a);
for(int i=0;i<len;i++) cout<<a[i]<<" ";
return 0;
}


一些补充:

目测这个算法只能计算没有重复字符的字符串

C++中长度为20的字符串几乎是我们能处理的极限(不考虑高精度,或者用JAVA写肯定是壕无压力处理几万长的字符串)

这里我们用数字来表示示例的顺序,实际中只要建立数字和字符的映射关系即可。这也是这个算法之所以强大之所在!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  字符串 算法 数学