康托展开
康托展开就是求一个字符串的字典序是这一串字符串的字符能组成的字符串中的第几名。(从小到大排列)。
时间复杂度
康托算法能在O(n2)的时间里求出该字典序的排名,用线段树维护的话能减少到O(nlogn)。
康拓展开的式子
int jc[10];//表示i的阶乘
jc[0]=1,jc[1]=1;
for(int i=2;i<10;i++){
jc[i]=jc[i-1]*i;
}
int cantor(char s[])
{
int ans=0,num=0;
int len=strlen(s);
for(int i=0;i<len;i++){
num=0;
for(int j=i+1;j<len;j++){
if(s[i]>s[j])
num++;
}
ans+=num*jc[len-1-i];
}
return ans;
}
ai表示的是,倒数第个i位置后面的数比倒数第i个位置的数小的个数。
即x则为这个字符串的排名-1,因为字典序最小的x为0。
为什么这个式子能求出排名
用 45231作为例子。
4后面比4小的数有 2 3 1。那么吧这三个数和4进行交换,那么后面4个数字随便排列都比之前的字典序要大,所以则是 3*(5-1)! ,以此类推。
逆康托展开
因为[an*(n-1)!]是绝对大于a(n-1)*(n-2)!的,所以我们求一个字符串的第x名的这个字符串。
就可以根据该公式来求,因为字典序最小时X为0,所以应该是求x-1.
即先求(x-1)/(n-1)!等于多少,即倒数第个i位置后面的数比倒数第i个位置的数小的个数。就可以根据这个个数,找到该位置的值。
如求一个长度为5的由12345组成的字符串的第55名,先求55/24=2,此时还未确定位置的数为 1 2 3 4 5 ,那么大于2个数的数为3,则第一个位置的数为3。
因为这个数确定了位置,那么接下来需要确定位置的数为 1 2 4 5。
需要求这个字符串的第55-4!*2名.
void decantor(int x, int n)
{
vector<int> rest; // 存放当前可选数,保证有序
vector<int> ans; // 所求排列组合
for(int i=1;i<=n;i++)
rest.push_back(i);
sort(rest.begin(),rest.end());
for(int i=n;i>=1;i--)
{
int r = x % jc[i-1];
int t = x / jc[i-1];
x = r;
ans.push_back(v[t]); // 剩余数里第t+1个数为当前位
rest.erase(v.begin()+t); // 移除选做当前位的数
}
}
刷题
https://www.luogu.com.cn/problem/P2730
https://vjudge.net/contest/441637#problem/U
https://vjudge.net/contest/441637#problem/S
https://vjudge.net/contest/441637#problem/H
题解
github题解