[CF1110H]Modest Substrings

description

CodeForces
定义一个正整数\(x\)是合适的当且仅当\(l\le x\le r\),其中\(l,r\le 10^{800}\)
找到一个长度为\(n\)的数字串,使其包含合适的数作为子串的次数最多,\(n\le 2000\)
如果有多解,输出字典序最小的那个。

solution

如果模式串的个数不多,那么直接套用AC自动机上数位dp的方法即可。
关于这一方法可参见[SDOI2014]数数
现在这个做法的缺陷是要放入的串太多。
考虑简化。
根据数位dp的思想,一个固定位数的数只要达到安全态,后面的数码可以随意选择,
因此我们将\(\ge l\)\(\le r\)的达到安全态的前缀都放入\(AC\)自动机;
设计\(dp\)状态为\(f[i][u]\),表示考虑了前\(i\)位,目前匹配到\(AC\)自动机的节点\(u\)时,已经能够匹配的子串个数。
对于后面的随意数码,由于题目限制长度为\(n\),所以只需要判断其长度是否\(>n-i-1\)即可。

code

实现代码长度居然达到\(3.5k\)...

#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define FL "a"
using namespace std;
typedef long long ll;
typedef long double dd;
const int N=2e3+10;
const int M=2e4+10;
const int inf=2147483647;
const dd pi=acos(-1);
const ll INF=1ll<<60;
inline ll read(){
  ll data=0,w=1;char ch=getchar();
  while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
  if(ch=='-')w=-1,ch=getchar();
  while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
  return data*w;
}
inline void file(){
  freopen(FL".in","r",stdin);
  freopen(FL".out","w",stdout);
}

char sl[N],sr[N];
int n,lenl,lenr,cnt,vis[M][10],fail[M],g[M][N],f[N][M];
inline void upd(int &a,int b){a=a>b?a:b;}
inline void getpre(){
  scanf("%s",sl+1);lenl=strlen(sl+1);
  scanf("%s",sr+1);lenr=strlen(sr+1);
  n=read();
  if(lenl==lenr){
    int ul,ur;ul=ur=0;
    for(int i=1;i<=lenl;i++)
      if(ul==ur){
	for(int c=sl[i]-48+1;c<sr[i]-48;c++){
	  if(!vis[ul][c])vis[ul][c]=++cnt;
	  g[vis[ul][c]][lenl-i]++;
	}
	if(!vis[ul][sl[i]-48])vis[ul][sl[i]-48]=++cnt;
	if(!vis[ur][sr[i]-48])vis[ur][sr[i]-48]=++cnt;
	ul=vis[ul][sl[i]-48];ur=vis[ur][sr[i]-48];
      }
      else{
	for(int c=sl[i]-48+1;c<10;c++){
	  if(!vis[ul][c])vis[ul][c]=++cnt;
	  g[vis[ul][c]][lenl-i]++;
	}
	for(int c=0;c<sr[i]-48;c++){
	  if(!vis[ur][c])vis[ur][c]=++cnt;
	  g[vis[ur][c]][lenr-i]++;
	}
	if(!vis[ul][sl[i]-48])vis[ul][sl[i]-48]=++cnt;
	if(!vis[ur][sr[i]-48])vis[ur][sr[i]-48]=++cnt;
	ul=vis[ul][sl[i]-48];ur=vis[ur][sr[i]-48];
      }
    g[ul][0]++;if(ul!=ur)g[ur][0]++;
  }
  else{
    int u;u=0;
    for(int i=1;i<=lenl;i++){
      for(int c=sl[i]-48+1;c<10;c++){
	if(!vis[u][c])vis[u][c]=++cnt;
	g[vis[u][c]][lenl-i]++;
      }
      if(!vis[u][sl[i]-48])vis[u][sl[i]-48]=++cnt;
      u=vis[u][sl[i]-48];
    }
    g[u][0]++;u=0;
    for(int i=1;i<=lenr;i++){
      for(int c=0;c<sr[i]-48;c++){
	if(!vis[u][c])vis[u][c]=++cnt;
	g[vis[u][c]][lenr-i]++;
      }
      if(!vis[u][sr[i]-48])vis[u][sr[i]-48]=++cnt;
      u=vis[u][sr[i]-48];
    }
    g[u][0]++;
    for(int i=lenl+1;i<lenr;i++)
      for(int c=1;c<10;c++){
	if(!vis[0][c])vis[0][c]=++cnt;
	g[vis[0][c]][i-1]++;
      }
  }
  for(int i=0;i<=cnt;i++)vis[0][0]=0;
}

inline void getfail(){
  static queue<int>Q;while(!Q.empty())Q.pop();
  for(int c=0;c<10;c++)if(vis[0][c])Q.push(vis[0][c]);
  while(!Q.empty()){
    int u=Q.front();Q.pop();
    for(int c=0;c<10;c++){
      int &v=vis[u][c];
      if(v){
	Q.push(v);fail[v]=vis[fail[u]][c];
	for(int i=0;i<=n;i++)g[v][i]+=g[fail[v]][i];
      }
      else v=vis[fail[u]][c];
    }
  }
  for(int u=0;u<=cnt;u++)
    for(int i=0;i<=n;i++)
      g[u][i]+=g[u][i-1];
}
inline void init(){
  getpre();
  getfail();
}

bool could[N][M];
inline void solve(){
  memset(f,128,sizeof(f));f[0][0]=0;
  for(int i=0;i<=n;i++)
    for(int u=0;u<=cnt;u++)
      if(f[i][u]>=0){
	f[i][u]+=g[u][n-i];
	for(int c=0;c<10;c++)
	  upd(f[i+1][vis[u][c]],f[i][u]);
      }
  int ans=0;
  for(int u=0;u<=cnt;u++)upd(ans,f[n][u]);
  printf("%d\n",ans);
  for(int u=0;u<=cnt;u++)if(f[n][u]==ans)could[n][u]=1;
  for(int i=n-1;~i;i--)
    for(int u=0;u<=cnt;u++)
      if(f[i][u]>=0)
	for(int c=0;c<10;c++)
	  if(could[i+1][vis[u][c]]&&
	     f[i+1][vis[u][c]]==f[i][u]+g[vis[u][c]][n-i-1]){
	    could[i][u]=1;break;
	  }
  assert(could[0][0]==1);
  int u=0;
  for(int i=0;i<n;i++)
    for(int c=0;c<10;c++)
      if(could[i+1][vis[u][c]]&&f[i+1][vis[u][c]]==f[i][u]+g[vis[u][c]][n-i-1]){
	putchar(48+c);u=vis[u][c];break;
      }
}

int main()
{
  init();
  solve();
  return 0;
}

posted @ 2019-02-08 11:55  cjfdf  阅读(637)  评论(0编辑  收藏  举报