全排列

该博客主要讨论字符串的全排列及其本质不同排列的计数方法。通过输入两个字符串,计算它们在所有全排列中按照字典序排序的相对位置差值。涉及到的算法包括递归、阶乘计算以及字符串比较。同时,文章提到了如何计算排列组合数,并给出了相应的C++代码实现。
摘要由CSDN通过智能技术生成

链接:https://ac.nowcoder.com/acm/contest/9680/J
来源:牛客网

白浅经过一段时间对字符串的学习之后,学会了如何使用递归输出一个字符串的所有全排列(虽然 C++ 中有自带的函数),甚至学会了如何输出所有去重后的全排列(即“本质不同的全排列”)!她拿到了一个字符串 S,现在她将 S 的所有本质不同的全排列按字典序大小进行排序。假如总共有 k 个本质不同的字符串,则这些字符串按照字典序大小分别编号为 1.2.3…k,。白浅在这些字符串中任取了两个字符串 A B,现在她想知道 A 的编号减去 B 的编号等于多少。
输入描述:
输入的第一行包含一个整数 n,代表字符串的长度。(1≤n≤2∗10^5)
接下来两行分别给出一个长度为 n 的字符串,分别代表 A, B。注意:字符串仅包含大写字母。
保证 A B 都是同一个字符串的全排列。答案对 10^9+7 取模。
输出描述:
输出一个整数代表结果
输入
4
ABBB
BBBA
输出
1000000004
有重复元素的全排列,n为全部元素的数目,n1…nk,为第一种元素到最后一种元素的数目,x为全排列本质不同的排列数目,n1!n2!n3!n4!..nk!x=n!
x=n!/(n1!n2!..nk!) (学习可见蓝书第二章2.1数学基础问题四(104页))。
对于A字符串找到前面有多少个比它小的数字cou1,B串找的前面比它小的数目cou2,二者相减取模。

#include <stdio.h>
#include <string.h>
char s1[1000001], s2[1000001], st[11];
long long int num[27], num1[27], num2[27], order1[27], cnt = 0, order[27], cou1 = 0, cou2 = 0, cou = 0;
long long int ans = 1, ans1, ans2, mod = 1000000007;
long long int qun(long long int a, long long int b)
{
    long long int ans = 1;
    while (b)
    {
        if (b & 1)
        {
            ans = ans * a;
            ans = ans % mod;
        }
        a = a * a;
        a %= mod;
        b >>= 1;
    }
    return ans;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
    freopen("in.txt", "r", stdin);
#endif
    long long int i, j, n;
    scanf("%lld", &n);
    fgets(st, 11, stdin);
    scanf("%s", s1);
    fgets(st, 11, stdin);
    scanf("%s", s2);
    for (i = 0; i < n; i++)
    {
        num[s1[i] - 'A']++;
        num1[s1[i] - 'A']++;
        num2[s1[i] - 'A']++;
    }
    for (i = 0; i < 26; i++)
    {
        if (num[i] > 0)
        {
            order[i] = cnt;
            order1[cnt] = i;
            cnt++;
        }
    }
    for (i = 2; i <= n; i++)
    {
        ans = (ans * i) % mod;
    }
    for (i = 0; i < 26; i++)
    {
        if (num[i] > 0)
        {
            for (j = 2; j <= num[i]; j++)
            {
                ans = ans * qun(j, mod - 2);
                ans = ans % mod;
            }
        }
    }
    ans1 = ans, ans2 = ans;
    for (i = 0; i < n; i++)
    {
        ans1 = ans1 * qun(n - i, mod - 2);
        ans1 %= mod;
        if (order[s1[i] - 'A'] != 0)
        {
            for (j = 0; j < order[s1[i] - 'A']; j++)
            {
                cou1 += ((ans1 * num1[order1[j]]) % mod);
                cou1 %= mod;
            }
        }
        ans1 *= num1[s1[i] - 'A'];
        ans1%=mod;
        num1[s1[i] - 'A']--;
    }
    for (i = 0; i < n; i++)
    {
        ans2 = ans2 * qun(n - i, mod - 2);
        ans2 %= mod;
        if (order[s2[i] - 'A'] != 0)
        {
            for (j = 0; j < order[s2[i] - 'A']; j++)
            {
                cou2 += ((ans2 * num2[order1[j]]) % mod);
                cou2 %= mod;
            }
        }
        ans2 *= num2[s2[i] - 'A'];
        ans2 %= mod;
        num2[s2[i] - 'A']--;
    }
    printf("%lld\n", ((cou1 - cou2) % mod + mod) % mod);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值