CF1110H Modest Substrings
Modest Substrings
You are given two integers \(L\) and \(R\) . Let's call an integer \(x\) modest, if \(L \le x \le R\) .
Find a string of length \(n\) , consisting of digits, which has the largest possible number of substrings, which make a modest integer. Substring having leading zeros are not counted. If there are many answers, find lexicographically smallest one.
If some number occurs multiple times as a substring, then in the counting of the number of modest substrings it is counted multiple times as well.
\(1\leq L\leq R\leq 10^{800},n\leq 2000\)。
题解
先只考虑\(\leq R\)的限制。回忆描述一个数\(\leq R\)需要满足的条件:
-
\(|x|< |R|\)。
只要这些长度\(< |R|\)的子串开头不是\(0\)就要统计进答案。
这个比较好做,直接在每个位置统计以它开头的子串的答案就行了。
-
\(|x|=|R|\),\(x\)和\(R\)的LCP的下一位满足\(x[i]< R[i]\)。
注意到这里需要知道\(x\)和\(R\)的LCP,所以考虑AC自动机。
我们只在这些串和\(R\)的LCP后第一个小于\(R\)的位置统计答案。那么只要在当前匹配到的节点选择的下一条出边小于\(R[i]\),并且我们要构造的串的剩余长度不小于某个下限,就要统计进答案。
每个AC自动机节点还要对fail树上的祖先特殊处理一下。
-
\(x=R\)。
这些串直接在向AC自动机终止节点转移的边上统计答案就行了。
回到原题要求\(L\leq x\leq R\)。我们直接用统计\(\leq R\)的减去统计\(\leq L-1\)的就行了。
时间复杂度\(O(n(|L|+|R|)\Sigma)\)。
CO int N=2e3+10;
char L[N],R[N];
int tot;
int ch[N][10],fa[N],val[N][10][N];
void insert(char str[],int n){
int x=0;
for(int i=1;i<=n;++i){
int c=str[i]-'0';
if(!ch[x][c]) ch[x][c]=++tot;
x=ch[x][c];
}
}
void build(char str[],int n,int v){
int x=0;
for(int i=1;i<=n;++i){
int c=str[i]-'0';
for(int d=x==0;d<c;++d) val[x][d][n-i+1]+=v; // edit 1: x=0 -> d starts from 1
if(i==n) val[x][c][1]+=v;
x=ch[x][c];
}
}
int F[N][N];
int main(){
scanf("%s",L+1);
int lenL=strlen(L+1);
if(L[lenL]>'0') --L[lenL];
else{
int p=lenL;
while(p>1 and L[p]=='0') --p;
--L[p],fill(L+p+1,L+lenL+1,'9');
}
if(L[1]=='0' and lenL>1)
copy(L+2,L+lenL+1,L+1),L[lenL--]=0;
if(L[1]>'0') insert(L,lenL); // edit 2: L=0 -> don't care about it
scanf("%s",R+1);
int lenR=strlen(R+1);
insert(R,lenR);
deque<int> que;
for(int c=0;c<=9;++c)if(ch[0][c]) que.push_back(ch[0][c]);
while(que.size()){
int x=que.front();que.pop_front();
for(int c=0;c<=9;++c){
if(!ch[x][c]) {ch[x][c]=ch[fa[x]][c]; continue;}
fa[ch[x][c]]=ch[fa[x]][c],que.push_back(ch[x][c]);
}
}
if(L[1]>'0') build(L,lenL,-1);
build(R,lenR,1);
int n=read<int>();
for(int x=1;x<=tot;++x)for(int c=0;c<=9;++c) // edit 3: x starts from 1
for(int i=1;i<=n;++i) val[x][c][i]+=val[fa[x]][c][i];
for(int x=0;x<=tot;++x)for(int c=1;c<=9;++c) // edit 4: c starts from 1
for(int i=1;i<lenL;++i) val[x][c][i]+=-1;
for(int x=0;x<=tot;++x)for(int c=1;c<=9;++c)
for(int i=1;i<lenR;++i) val[x][c][i]+=1;
for(int x=0;x<=tot;++x)for(int c=0;c<=9;++c)
for(int i=1;i<=n;++i) val[x][c][i]+=val[x][c][i-1];
// for(int x=0;x<=tot;++x)for(int c=0;c<9;++c)
// for(int i=1;i<=n;++i) cerr<<x<<" "<<c<<" "<<i<<" v="<<val[x][c][i]<<endl;
for(int i=n-1;i>=0;--i)for(int x=0;x<=tot;++x){
F[i][x]=-1e9; // edit 5: initialize F=-inf
for(int c=0;c<=9;++c) F[i][x]=max(F[i][x],F[i+1][ch[x][c]]+val[x][c][n-i]);
}
printf("%d\n",F[0][0]);
for(int i=0,x=0;i<n;++i){
int c=-1;
for(int d=0;d<=9;++d)
if(F[i][x]==F[i+1][ch[x][d]]+val[x][d][n-i]) {c=d; break;}
printf("%d",c);
x=ch[x][c];
}
return 0;
}