[Jzoj] 4812.string

题目大意

给出一个长度为NNN, 由小写英文字母组成的字符串SSS, 求在所有由小写英文字母组成且长度为NNN且恰好有KKK位与SSS不同的字符串中,给定字符串TTT按照字典序排在第几位。
由于答案可能很大,模109+710^9 + 7109+7 输出。

题目解析

给定的字符串TTT实际上是取字典序小于TTT的所有字符串

先预处理出CiC_iCi,表示前iii位中,SSS串与TTT串之间的差异数

我们枚举每一位Ai(Ai&lt;Ti)A_i(A_i&lt;T_i)Ai(Ai<Ti),当AiA_iAiSiS_iSi不同时,则iii位之后的允许的位数不同=K−C[i−1]−1=K-C[i-1]-1=KC[i1]1,表示总共可以有KKK个差异,减去i−1i-1i1前有多少位的差异再减去当前第iii位不同,所剩余的差异数XXX给剩下的串。Cab∗25a(a=X,b=n−i)C_a^b*25^a(a=X,b=n-i)Cab25a(a=X,b=ni)

依次类推,就可以得出答案了。

因为要用到组合数学,又要除又要取模,所以要用乘法逆元,即a/b=a∗bpa/b=a*b^pa/b=abp−^-2^22(mod(mod(mod p)p)p)

代码

#include<bits/stdc++.h>
#define N 100005
#define M 1000000007
#define LL long long
using namespace std;
int n,k,ans;
LL a[N],b[N],p[N],ny[N],c[N],s[N];
string s1,s2;
LL qp(LL a,LL b)
{
	LL c=1;
	while(b)
	{
	  if(b&1) (c*=a)%=M;
	  (a*=a)%=M;
	  b>>=1;
	}
	return c%M;
}
int main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	p[0]=ny[0]=c[0]=1;
	for(int i=1;i<=100000;i++) p[i]=(p[i-1]*25)%M,c[i]=(c[i-1]*i)%M,ny[i]=qp(c[i],M-2);
	cin>>n>>k>>s1>>s2;
	for(int i=1;i<=n;i++) a[i]=s1[i-1]-'a',b[i]=s2[i-1]-'a';
	for(int i=1;i<=n;i++) if(a[i]!=b[i]) s[i]=s[i-1]+1; else s[i]=s[i-1];
	for(int i=1;i<=n;i++)
	 for(int j=0;j<26;j++)
	  if(j<b[i])
	  {
	  	int x=k-s[i-1];
	  	if(j!=a[i]) x--;
	  	(ans+=(((c[n-i]*ny[x])%M)*ny[n-i-x]%M)*p[x]%M)%=M;
	  }
	cout<<(ans+1)%M;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值