题目:给你一个字符串,判断他是这些字符组成的全排列中按字典序排序的第几位。
分析:组合数学。康托展开,枚举长度求和即可。
首先,考虑非重集,从前向后扫描,每一位找到前面相同本位小于对应值得串个数;
例如:f(cba)= 2*2!+ 1*1!= 5,对应第6个串;
(c**对应a、b开头的比他小,cb*对应ca开头的比他)
然后,考虑重集,对应位置取相同的元素相同,剩余的元素要去掉相同的全排列;
例如:f(cabaa)= (4!/3!+ 4!/2!)+ (0) + (2!/1!)+(0) = 18,对应第19个串;
(c****对应b开头为4!/3!(aaa)、a开头为4!/2!(aa),cab**对应caa开头为2!/1!(a))
注意,这里因为30!超过了long long,使用数组存所有的因数,利用gcd约分就得到int范围内的值了。
说明:还没有到600题╮(╯▽╰)╭。
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> using namespace std; int gcd(int a, int b) { return a%b?gcd(b, a%b):b; } int main() { int letter[128]; int u[200],d[200]; char buf[32]; while (~scanf("%s",buf)) { if (!strcmp("#", buf)) break; memset(letter, 0, sizeof(letter)); for (int i = 0; buf[i]; ++ i) letter[buf[i]] ++; int len = strlen(buf),count = 1; for (int i = 0; i < len; ++ i) { int d_count = 0,u_count = 0; for (int j = 'a'; j <= 'z'; ++ j) for (int k = 2; k <= letter[j]; ++ k) d[d_count ++] = k; for (int k = 2; k <= len-1-i; ++ k) u[u_count ++] = k; for (int j = 'a'; j < buf[i]; ++ j) { if (letter[j]) { int g = 1,value = 1; u[u_count] = letter[j]; for (int p = 0; p <= u_count; ++ p) for (int q = 0; q < d_count; ++ q) { g = gcd(u[p], d[q]); u[p] /= g; d[q] /= g; } for (int p = 0; p <= u_count; ++ p) value *= u[p]; count += value; } } letter[buf[i]] --; } printf("%10d\n",count); } return 0; }
时间: 2024-10-07 18:57:50