CF666C Codeword 组合数

题意:

戳这里

分析:

  • 暴力

(O(n^2)) 的 DP ,(f[i][j]=f[i-1][j-1]+f[i][j-1]*25)

  • 正解

我们发现 DP 方程与原串的形态无关,然后观察(题解) 式子,发现它可以通过组合数优化

(f_{i,j}=C_{i-1}^{j-1} imes 25^{j-i}+C_i^{j-1} imes 26)

这个递推式的含义就是:

  1. (j-1) 位匹配上了原来 (i-1) 个,为了避免算重,每一位的取值不能等于下一个要匹配的数,所以取值种类有 25 种,空余位置有 (j-i)

  2. (j-1) 位匹配完了,之后随意取

然后我们对于长度对应的答案进行记忆化,由于不同长度的串的种类数最多只有 (sqrt{10^5}) 种,所以复杂度是 (O(nsqrt{n}))

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 1e5+5;
	const int mod = 1e9+7;
	char ch[maxn];
	vector<int> f[maxn];
	int fac[maxn],inv[maxn],pw[maxn];
	bool vis[maxn];
	int n,len;
	
	void init()
	{
		fac[0]=fac[1]=inv[0]=inv[1]=pw[0]=1;
		pw[1]=25;
		for(int i=2;i<=100000;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod,pw[i]=1ll*pw[i-1]*25%mod;
		for(int i=2;i<=100000;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod;
	}
	
	long long C(long long n,long long m)
	{
		return 1ll*fac[n]*inv[n-m]%mod*inv[m]%mod;
	}
	
	void solve()
	{
		if(!vis[len])
		{
			vis[len]=true;
			for(int i=0;i<len;i++) f[len].push_back(0);
			for(int i=len;i<=100000;i++) f[len].push_back((1ll*C(i-1,len-1)*pw[i-len]%mod+1ll*f[len][i-1]*26%mod)%mod);
		}
		printf("%d
",f[len][read()]%mod);
	}
	
	void work()
	{
		init();
		n=read();
		scanf("%s",ch);len=strlen(ch);
		for(int i=1;i<=n;i++) 
		{
			if(read()==1) 
			{
				scanf("%s",ch);
				len=strlen(ch);
			}
			else solve();
		}
	}

}

int main()
{
	zzc::work();
	return 0;
}

原文地址:https://www.cnblogs.com/youth518/p/14084749.html