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;
}