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 }

浙公网安备 33010602011771号








