【CF666C】Codeword 結論題+暴力

【CF666C】Codeword

題意:一開始有一個字符串s,有m個事件,每一個事件形如:node

1.用一個新的字符串t來替換s
2.給出n,問有多少個長度爲n的小寫字母組成的字符串知足包含s做爲其一個子序列?答案$\mod 10^9+7$
ios

$m,n,\sum |t|\le 10^5$spa

題解:有一個結論:答案只與n和|s|有關,與s究竟是什麼無關。咱們只考慮s在母串中第一次出現的位置。設$|s|=k$,假如s的每一個字符出現的位置分別是$p_1p_2...p_k$,則對於$i\in [1,k]$,$(p_{i-1},p_i)$之間的字符都不能是$s_i$,因此這些位置都有25種可能。而後咱們就能夠將咱們發現的結論形式化的寫出來了。咱們枚舉$p_k$的位置,則有:blog

$ans=\sum\limits_{i=k}^{n}C_{i-1}^{k-1}\alpha^{n-i}(\alpha-1)^{i-k}$事件

可是若是咱們每次都暴力計算的話複雜度難以接受。不過咱們發現本質不一樣的|s|只有$\sqrt n$種,因此咱們去重,而後將式子改寫爲:字符串

$ans=\alpha^{n}\sum\limits_{i=k}^nC_{i-1}^{k-1}\alpha^{-i}(\alpha-1)^{i-k}$get

咱們對於每一個|s|都預處理出後面那些東西,即可作到$O(1)$回答詢問,時間複雜度$O(m\sqrt n)$。string

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=100010;
const ll P=1000000007;
int m,N,tot;
struct node
{
	int n,len,org;
}p[maxn];
char str[maxn];
ll jc[maxn],ine[maxn],jcc[maxn],q[maxn],q1[maxn],qi[maxn],s[maxn],ans[maxn];
bool cmp(const node &a,const node &b)
{
	return a.len<b.len;
}
inline ll c(int a,int b)
{
	if(a<b)	return 0;
	return jc[a]*jcc[a-b]%P*jcc[b]%P;
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
	return ret*f;
}
int main()
{
	m=rd(),scanf("%s",str),N=100000;
	int i,j,a=strlen(str),b;
	for(i=1;i<=m;i++)
	{
		if(rd()==1)	scanf("%s",str),a=strlen(str);
		else	b=rd(),p[++tot].len=a,p[tot].n=b,p[tot].org=tot;
	}
	ine[0]=ine[1]=jc[0]=jc[1]=jcc[0]=jcc[1]=1;
	for(i=2;i<=N;i++)	jc[i]=jc[i-1]*i%P,ine[i]=P-(P/i)*ine[P%i]%P,jcc[i]=jcc[i-1]*ine[i]%P;
	for(q[0]=q1[0]=qi[0]=i=1;i<=N;i++)	q[i]=q[i-1]*26%P,q1[i]=q1[i-1]*25%P,qi[i]=qi[i-1]*ine[26]%P;
	sort(p+1,p+tot+1,cmp);
	for(i=1;i<=tot;i++)
	{
		a=p[i].len,b=p[i].n;
		if(a!=p[i-1].len)
		{
			memset(s,0,sizeof(s[0])*a);
			for(j=a;j<=N;j++)	s[j]=(s[j-1]+q1[j-a]*qi[j]%P*c(j-1,a-1))%P;
		}
		ans[p[i].org]=q[b]*s[b]%P;
	}
	for(i=1;i<=tot;i++)	printf("%lld\n",ans[i]);
	return 0;
}
相關文章
相關標籤/搜索