CF585F Digits of Number Pi

Digits of Number Pi

给定一个数字串 𝑠,可能存在前导零。

定义一个长度为 𝑑 的数字串 𝑡 是 “半出现的”,当且仅当存在一个长度为 ⌊𝑑/2⌋ 的 𝑠 和 𝑡 的公共子串。

给定两个无前导零的 𝑑 位的数字 𝑥, 𝑦(𝑥 < 𝑦),问 [𝑥, 𝑦] 中所有的整数所代表的数字串中,有多少是 “半出现的”。

答案对 109 + 7 取模。

1 ≤ 𝑠 ≤ 1000,2 ≤ 𝑑 ≤ 50。

题解

陆宏《动态规划》。

先建一棵 AC 自动机,把 𝑆 的所有长度为 ⌊𝑑/2⌋ 的子串插到 AC 自动机里。

𝑓[𝑖][𝑗][0/1][0/1] 表示考虑了前 𝑖 位,目前匹配到了 AC 自动机上的 𝑗 号节点,前 𝑖 位是否取到下界(𝑥),是否取到上界(𝑦)。

枚举当前这位填哪个数转移。

关于怎么统计,有两种做法。第一种是正难则反,第二种是令所有终止节点的出边指向该终止节点。

复杂度 𝑂(10𝑑2|𝑆|) 。

char S[1002],X[52],Y[52];
int ch[25000][10],fa[25000],val[25000],tot;
int F[51][25000][2][2];

int main(){
	scanf("%s",S+1);
	int n=strlen(S+1);
	scanf("%s%s",X+1,Y+1);
	int d=strlen(X+1);
	if(n<d/2) {puts("0"); return 0;}
	
	for(int i=1;i+d/2-1<=n;++i){
		int x=0;
		for(int j=i;j<=i+d/2-1;++j){
			int c=S[j]-'0';
			if(!ch[x][c]) ch[x][c]=++tot;
			x=ch[x][c];
		}
		val[x]=1;
	}
	
	deque<int> Q;
	for(int c=0;c<=9;++c)if(ch[0][c]) Q.push_back(ch[0][c]);
	while(Q.size()){
		int x=Q.front();Q.pop_front();
		for(int c=0;c<=9;++c){
			if(!ch[x][c]) ch[x][c]=ch[fa[x]][c];
			else fa[ch[x][c]]=ch[fa[x]][c],Q.push_back(ch[x][c]);
		}
	}
	for(int x=0;x<=tot;++x)if(val[x])
		fill(ch[x],ch[x]+10,x);
	
	F[0][0][1][1]=1;
	for(int i=1;i<=d;++i)for(int x=0;x<=tot;++x)
		for(int l=0;l<=1;++l)for(int r=0;r<=1;++r){
			if(!F[i-1][x][l][r]) continue;
			for(int c=0;c<=9;++c){
				if(l and c<X[i]-'0') continue;
				if(r and c>Y[i]-'0') continue;
				chkadd(F[i][ch[x][c]][l and c==X[i]-'0'][r and c==Y[i]-'0'],F[i-1][x][l][r]);
			}
		}
//	for(int i=0;i<=d;++i)for(int x=0;x<=tot;++x)
//		for(int l=0;l<=1;++l)for(int r=0;r<=1;++r)if(F[i][x][l][r])
//			cerr<<i<<" "<<x<<" "<<l<<" "<<r<<" F="<<F[i][x][l][r]<<endl;
	int ans=0;
	for(int x=0;x<=tot;++x)if(val[x])
		for(int l=0;l<=1;++l)for(int r=0;r<=1;++r)
			chkadd(ans,F[d][x][l][r]);
	printf("%d\n",ans);
	return 0;
}

posted on 2020-04-22 09:23  autoint  阅读(182)  评论(0编辑  收藏  举报

导航