传送门
【题目分析】
第一次知道数位DP可以和AC自动机套起来用。。。Orz
二分是很明显的,可以先计算L前的数量+k再二分,这样不用考虑下界(但我写的是考虑下界,所以跑一次就完了)。
然后就是考虑如何统计是否有子串出现,这个地方显然不能强行枚举,但这个问题其实就是询问文本串是否在当前串中出现过,赤裸裸的AC自动机,转移的时候直接在AC自动机上判下一位是否有结束标记即可。
【代码~】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=2e3+10;
int n,siz;
int up[21],low[21],num[21],len;
LL dp[21][MAXN][2];
LL x,y,l,r,mid,k,a;
LL ans;
struct ac_at{
int ch[10];
int val,fail;
}ac[MAXN];
LL Read(){
LL i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
void insert(){
int now=0;
for(int i=len;i;--i){
if(!ac[now].ch[num[i]])
ac[now].ch[num[i]]=++siz;
now=ac[now].ch[num[i]];
}
ac[now].val=1;
}
void build_fail(){
queue<int> q;
for(int i=0;i<=9;++i){
int k=ac[0].ch[i];
if(k)
ac[k].fail=0,q.push(k);
}
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<=9;++i){
int k=ac[u].ch[i];
if(k){
ac[k].fail=ac[ac[u].fail].ch[i];
ac[k].val|=ac[ac[k].fail].val;
q.push(k);
}
else
ac[u].ch[i]=ac[ac[u].fail].ch[i];
}
}
}
LL dfs(int now,int last,int had,int lim1,int lim2,int zer){
if(!now)
return had;
if(!lim1&&!lim2&&!zer&&dp[now][last][had]!=-1)
return dp[now][last][had];
LL ret=0;
int xx=lim1?low[now]:0,sx=lim2?up[now]:9;
for(int i=xx;i<=sx;++i){
if(zer&&!i&&now>1){
ret+=dfs(now-1,0,had,lim1&&i==xx,lim2&&i==sx,1);
}
else{
ret+=dfs(now-1,ac[last].ch[i],had|ac[ac[last].ch[i]].val,lim1&&i==xx,lim2&&i==sx,zer&&!i);
}
}
if(!lim1&&!lim2&&!zer)
dp[now][last][had]=ret;
return ret;
}
LL calc(LL L,LL R){
len=0;
while(R){
up[++len]=R-R/10*10;
R/=10;
}
int len2=0;
while(L){
low[++len2]=L-L/10*10;
L/=10;
}
for(int i=len2+1;i<=len;++i)
low[i]=0;
return dfs(len,0,0,1,1,1);
}
int main(){
x=Read(),y=Read(),k=Read(),n=Read();
for(int i=1;i<=n;++i){
a=Read();
len=0;
while(a){
num[++len]=a-a/10*10;
a/=10;
}
insert();
}
build_fail();
memset(dp,-1,sizeof(dp));
l=x,r=ans=y+1;
while(l<=r){
mid=l+r>>1;
if(calc(x,mid)<k)
l=mid+1;
else
r=mid-1,ans=mid;
}
if(ans==y+1)
puts("no such number");
else
cout<<ans;
return 0;
}