2017年8月15日提高组T1 字符串

23 篇文章 0 订阅

Description


有两个长度为n且仅由小写字母组成的字符串S,T,满足S和T恰好有k位不同。问在所有恰好与S有k位不同的字符串中,T按照字典序排在第几位。由于答案可能很大,模10^9+7输出。

Input


第一行两个整数n,k。
第二行一个字符串S。
第三行一个字符串T。

Output


一行一个整数表示答案。

Hint


对于前30%的数据,n<=5.
对于100%的数据,k<=n<=100000.

Source


BY BPM

Solution


看到这种类型的题目毫无想法,几乎没有接触过。。

首先说一个结论,与长度为n的串有k位不同的串共有 Ckn25k 个,这玩意推一推就出来了。

想要知道T的排名,只需要计算前面有多少个串就可以了。考虑当前做到i位,那么如果S的第i位比T的第i位要小,那么第一位对答案的贡献就是
Cn1m125m1(T[1]a1)+Cn1m25m 然后要m–

如果S的第i位比T的第i位要大,那么第一位对答案的贡献就是 Cn1m125m1(T[1]a) 然后要m–

如果S的第i位等于T的第i位,那么对答案的贡献就是 Cn1m125m1(T[1]a)
然后往下一位一样的接着处理就好了。

考虑到要对组合数取模,即对分数取模,这里用到了乘法逆元解决,即 ab%c abϕ(c)1%c 是等价的,这里1e9+7是质数,所以 ϕ(109+7)=109+6 。快速幂搞定

Code


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <queue>
#include <vector>
#include <algorithm>
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define drp(i, st, ed) for (int i = st; i >= ed; i -= 1)
#define erg(i, st) for (int i = ls[st]; i; i = e[i].next)
#define fill(x, t) memset(x, t, sizeof(x))
#define max(x, y) (x)>(y)?(x):(y)
#define min(x, y) (x)<(y)?(x):(y)
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define N 100001
#define E 50001
#define L 256
char s[N], t[N];
ll biao[N];
inline ll pw(int dep, ll x){
    if (dep == 0){return 1;}
    if (dep == 1){return x;}
    ll tmp = pw(dep / 2, x);
    if (dep & 1){return ((tmp*tmp)%MOD*x)%MOD;}
    return (tmp*tmp)%MOD;
}
inline ll C(int n, int m){
    if (n < 0 || m < 0 || n < m){return 0;}
    ll tmp = pw(MOD - 2, biao[n-m]*biao[m]%MOD);
    (tmp *= biao[n])%=MOD;
    (tmp *= pw(m, 25))%=MOD;
    return tmp;
}
int main(void){
    int n, m;
    scanf("%d %d",&n,&m);
    scanf("%s", s);
    scanf("%s", t);
    biao[0] = 1;
    rep(i, 1, n){
        biao[i] = biao[i - 1] * i%MOD;
    }
    ll ans = 0;
    rep(i, 0, n - 1){
        if (s[i] < t[i]){
            (ans += C(n-i-1,m)%MOD)%=MOD;
            (ans += (t[i]-'a'-1)*C(n-i-1,m-1)%MOD)%=MOD;
        }
        else{
            (ans += (t[i]-'a')*C(n-i-1,m-1)%MOD)%=MOD;
        }
        m -= (s[i] != t[i]);
    }
    printf("%lld\n", (ans+1)%MOD);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值