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;
}
静渊以有谋,疏通而知事。