CF1110H - Modest Substrings

CF1110H - Modest Substrings

题意:给你l,r,n。让你求一个长为n的数字串(允许前导0),使得这个数字串中好的子串最多。输出个数和字典序最小的解。好的子串定义为没有前导0且在[l, r]之间。

1 <= l, r <= 10800,n <= 2000。

解:有个很naive的想法就是把l到r的每个数扔到AC自动机里然后DP。

然后根据数位DP我们发现,当走到某一位的时候,接下来无论怎么走都行。AC自动机那个节点下面是一个满10叉树。

于是我们把这里压缩了......具体来说,对于AC自动机上每一个点,我们记一个g[],gi表示从这个点出发向后走i步,能否到达一个终止状态(好的子串)。

然后又记hi为沿着fail指针把gi往下推的答案。因为你从fail指针出发走i步能得到的好的子串,从当前节点走i步同样能得到,且它们开头位置不同。因为fail是当前节点的后缀。

最后记sumi为每个点的hi的前缀和。因为如果你走了i步,那么你一定会走i - 1步,它们结束位置不同。

这时就可以AC自动机上DP了。由于有个字典序的限制,我们可以尝试从后往前DP。设fij表示在j号节点,继续走i步,能得到的最优的串含有多少个好的子串。转移的时候枚举这个节点下一步走到哪,然后加上sum[j][i]。

初始状态是f[0][j] = sum[j][0]。

现在考虑如何构建这个AC自动机。我们直接数位DP的同时记录在AC自动机上走到哪里了。数位DP返回的时候给AC自动机当前节点的sum赋上1。

  1 #include <bits/stdc++.h>
  2 
  3 const int N = 2010, INF = 0x3f3f3f3f;
  4 
  5 char L[N], R[N];
  6 int n, tr[N * 10][10], len, tot = 1, sum[N * 10][N], fail[N * 10];
  7 int f[N][N * 10], fr[N][N * 10];
  8 std::queue<int> Q;
  9 
 10 void DFS(int i, bool up, bool down, int x) {
 11     if(!up && !down) {
 12         sum[x][len - i + 1] = 1;
 13         return;
 14     }
 15     if(i == len + 1) {
 16         sum[x][0] = 1;
 17         return;
 18     }
 19     int op = down ? L[i] - '0' : 0;
 20     int ed = up ? R[i] - '0' : 9;
 21     for(int j = op; j <= ed; j++) {
 22         int y = x;
 23         if(x == 1 && !j) {
 24             ;
 25         }
 26         else {
 27             if(!tr[x][j]) {
 28                 tr[x][j] = ++tot;
 29             }
 30             y = tr[x][j];
 31         }
 32         DFS(i + 1, up && (j == R[i] - '0'), down && (j == L[i] - '0'), y);
 33     }
 34     return;
 35 }
 36 
 37 inline void build() {
 38     Q.push(1);
 39     fail[1] = 1;
 40     while(Q.size()) {
 41         int x = Q.front();
 42         Q.pop();
 43         for(int f = 0; f <= 9; f++) {
 44             if(tr[x][f]) {
 45                 int y = tr[x][f];
 46                 if(x == 1) {
 47                     fail[y] = 1;
 48                 }
 49                 else {
 50                     fail[y] = tr[fail[x]][f];
 51                 }
 52                 for(int i = 0; i <= n; i++) {
 53                     sum[y][i] += sum[fail[y]][i];
 54                 }
 55                 Q.push(y);
 56             }
 57             else {
 58                 if(x == 1) {
 59                     tr[x][f] = 1;
 60                 }
 61                 else {
 62                     tr[x][f] = tr[fail[x]][f];
 63                 }
 64             }
 65         }
 66     }
 67     return;
 68 }
 69 
 70 int main() {
 71     
 72     scanf("%s%s%d", L + 1, R + 1, &n);
 73     len = strlen(R + 1);
 74     int len1 = strlen(L + 1);
 75     if(len != len1) {
 76         int d = len - len1;
 77         for(int i = len1; i >= 1; i--) {
 78             L[i + d] = L[i];
 79         }
 80         for(int i = d; i >= 1; i--) {
 81             L[i] = '0';
 82         }
 83     }
 84     
 85     DFS(1, true, true, 1);
 86     build();
 87     
 88     for(int i = 1; i <= tot; i++) {
 89         for(int j = 1; j <= n; j++) {
 90             sum[i][j] += sum[i][j - 1];
 91         }
 92     }
 93     
 94     for(int i = 1; i <= tot; i++) {
 95         f[0][i] = sum[i][0];
 96     }
 97     
 98     for(int i = 1; i <= n; i++) {
 99         for(int j = 1; j <= tot; j++) {
100             f[i][j] = -INF; 
101             for(int c = 0; c <= 9; c++) {
102                 int y = tr[j][c];
103                 if(f[i][j] < f[i - 1][y]) {
104                     f[i][j] = f[i - 1][y];
105                     fr[i][j] = c;
106                 }
107             }
108             f[i][j] += sum[j][i];
109         }
110     }
111     
112     printf("%d\n", f[n][1]);
113     int p = 1;
114     for(int i = n; i >= 1; i--) {
115         putchar(fr[i][p] + '0');
116         p = tr[p][fr[i][p]];
117     }
118     puts(""); 
119     
120     return 0;
121 }
AC代码

 

posted @ 2019-06-07 21:21  huyufeifei  阅读(323)  评论(0)    收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜