变进制数

全排列哈希
我们熟知的数一般都是常进制数,所谓常进制数就是该数的每一位都是常数进制:

  • k进制数上的每一位都逢k进一, 第i位的位权是 k i k^i ki
    这里要介绍一种变进制数,用来表示字符串的排列状态
  • 这种数的第i位逢i进一,第i位的位权是 i ! i! i!
  • 用d[i]来表示一个变进制数第i位上的数字
  • 一个n位变进制数的值就为 ∑ i = 0 n − 1 d [ i ] × i ! \sum\limits^{n - 1}_{i=0}d[i]×i! i=0n1d[i]×i!
    这是一个最大的9位变进制数

876543210

它对应的十进制数为

8 × 8! + 7 × 7! + 6 × 6! + …… + 1 × 1! + 0 × 0! = 9! - 1 = 362879

我们可以找到一个9位变进制数,与一个9位无重复串的某种排列一一对应

用d[i]表示字符串中的第i位与其前面的字符组成的逆序对个数

字符串的一种排列对应的变进制数的值为 ∑ i = 0 n − 1 d [ i ] × i ! \sum\limits^{n−1}_{i=0}d[i]×i! i=0n1d[i]×i!
这是字符串123x46758的与d[i]d[i]的对应关系

i 0 1 2 3 4 5 6 7 8
s[i] 1 2 3 x 4 6 7 5 8
d[i] 0 0 0 0 1 1 1 3 1
它对应的变进制数的值为
1 × 4! + 1 × 5! + 1 × 6! + 3 × 7! + 1 × 8! = 56304
因此可以用以下函数求字符串的一种排列对应的哈希值

int permutation_hash(char s[], int n)       //求长度为n的字符串某种排列的哈希值
{
    int ans = 0;
    for(int i = 0; i < n; i ++)
    {
        int d = 0;
        for(int j = 0; j < i; j ++)
            if(s[j] > s[i])  d ++;          //求s[i]与其前面的字符组成的逆序对个数
        ans += d * fact[i];
    }
    return ans;
}

n不能太大,通常不超过12,否则会溢出
时间复杂度为O(n²)

你可能感兴趣的:(Acwing学习笔记)