[jzoj 4812]【NOIP2016提高A组五校联考2】string

Description
给出一个长度为n, 由小写英文字母组成的字符串S, 求在所有由小写英文字母组成且长度为n 且恰好有k 位与S 不同的字符串中,给定字符串T 按照字典序排在第几位。 由于答案可能很大,模10^9 + 7 输出。
Input
第一行为两个整数n; k
第二行一个字符串S
第三行一个字符串T,(T即是k位与S不同的串)
Output
输出一行取模后的答案。
Sample Input
4 1
abcd
bbcd
Sample Output
76
Data Constraint
对于前30% 的数据,n<=5
对于100% 的数据,k<=n<=10^5

The Solution

对于 30%的数据,毫无疑问暴力即可。
对于100%的数据,我们可以用组合数。

题目大意可以转换成:
问题是要求有多少个字符串的字典序小于T且有k位与S不同。
最后答案就加1即可。

我们先枚举第一位,若第一位比T1小,则第2~n位填什么字母进去都可以,
用组合数求出可行方案。
若第一位等于T2,则我们枚举第二位, 有两种情况,
第一种:第二位比T2小,第3~n位填什么都可以,
一样用组合数求出方案数。
第二种第二位等于T2,那么我们就枚举第三位,
如此类推下去到最后,求出答案即可。

打的时候组合数要注意一点,我就是一开始把 Cnm 中n,m的位置弄反了,mdzz。。。

CODE

#include <cstdio>
#include <iostream>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define N 100005
#define mo 1000000007

using namespace  std; 

typedef long long ll;

char S[N],T[N],ch;
int G[N],word[N],F[N],ans=1,n,k;


int mi(int x,int y)
{
    int z=1;
    for (;y;x=(ll)x * x % mo,y >>= 1) if (y&1) z = (ll)z * x % mo;
    return z; 
}

int Calc(int n,int m) {return (ll)F[m] * G[n] % mo * (ll)G[m-n] % mo;}

int main()
{
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    F[0] = word[0] = 1;
    scanf("%d%d",&n,&k);
    scanf("%s",S+1); scanf("%s",T+1);
    fo(i,1,n) S[i] = S[i] - 'a',T[i] = T[i] - 'a';
    fo(i,1,n)
    {
        F[i]=(F[i-1]*(ll)i) %mo;
        word[i]=(word[i-1]*(ll)25)%mo;
    }
    G[n]=mi(F[n],mo-2);
    fd(i,n-1,0) G[i]=(G[i+1]*(ll)(i+1))%mo;
    fo(i,1,n)
        if (S[i] == T[i]) ans=((ll)ans+(ll)T[i]*word[k-1]%mo*Calc(k-1,n-i))%mo;
        else 
        if (S[i] < T[i])
        {
            ans=((ll)ans+(ll)(T[i]-1)*word[k-1]%mo*Calc(k-1,n-i))%mo;
            if (k <= n-i) ans=((ll)ans+(ll)word[k]*Calc(k,n-i)%mo)%mo;
            k--;
        }
        else if (S[i] > T[i]) ans=((ll)ans+(ll)T[i]*word[k-1]%mo*Calc(k-1,n-i))%mo,k--;
        else break;
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值