LightOJ 1060 nth Permutation(组合数--k大字典序)

题目

给一串长度不超过20的字符串,求n-th permutation of the string. 0<n<231

思路

先排序,求出当前串有K种组合,如果n大于k,显然impossible;
然后就是每个位置枚举字符,判断下合理性就行了;

char s[30];
long long f[22];
typedef struct item {
    char c;
    int num;
    item() {c = '\0';num = 0;}
}item;
item p[20];
int tot;

void init() {
    f[0] = 1;
    for (int i = 1;i < 21;++i)
        f[i] = 1ll*f[i-1]*i;
    // Rep(i, 1, 20) cout << f[i] << endl;
}

long long calc() {
    LL t1 = 0, t2 = 1;
    for (int i = 0;i <= tot;++i) {
        t1 += p[i].num;
        t2 *= f[p[i].num];
    }
    // cout << f[t1] << ' ' << t2 << endl;
    return f[t1] / t2;
}

void solve(int k, int len) {
    if (calc() < k) {
        printf("Impossible\n");
        return ;
    }

    for (int i = 1;i <= len;++i) {
        long long t = 0;
        int j;
        for (j = 0;j <= tot;++j) {
            p[j].num--;
            long long now = calc();
            t += now;
            if (t >= k) {
                putchar(p[j].c);
                k -= (t - now);
                break;
            }
            p[j].num++;
        }
        if (p[j].num == 0) {
            for (int o = j;o < tot;++o)
                p[o] = p[o+1];
            --tot;
        }
    }
    puts("");
}
int main(int argc, const char * argv[])
{   int kase;cin >> kase;
    init();
    while(kase--) {
        int k;
        scanf("%s%d", s + 1,&k);
        int len = strlen(s + 1);
        sort(s + 1, s + 1 + len);
        tot = 0;
        p[tot].c = s[1];
        p[tot].num = 1;
        for (int i = 2;i <= len;++i) {
            if (s[i] == s[i - 1]) p[tot].num++;
            else {
                ++tot;
                p[tot].c = s[i];
                p[tot].num = 1;
            }
        }
        printf("Case %d: ", ++nCase);
        solve(k, len);
    }
    // showtime;
    return 0;
}

你可能感兴趣的:(-----数学-----)