[SDOI2014]数数
这题的前置知识是AC自动机和dp,前置题目是 [JSOI2007]文本生成器,前置题目我写的题解 题解-[JSOI2007]文本生成器。我的讲解假设你做过上面那道题。
这题比上面那题多个条件,我因此多调了 3 3 3 个小时。多的条件:答案要不大于整数 n n n。所以AC自动机部分同上,改变dp部分。
解: d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 表示文本串(幸运数)长度为 i i i,结尾是AC自动机上的节点 j j j, k k k 表示这个文本串下一个字符是否受 n n n 某个数位大小的限制(如果受限制, k = 1 k=1 k=1;否则, k = 0 k=0 k=0)。 m k [ i ] mk[i] mk[i] 表示 i i i 这个AC自动机上节点是否为某个不幸运的数结尾。
仔细读题会发现:模式串中含有 0 0 0 前置,而文本串不能以 0 0 0 开头。所以有(所有数组下标从 1 1 1 开始, 1 ≤ n [ 1 ] ≤ 9 1\le n[1]\le 9 1≤n[1]≤9,因为 c h [ 1 ] [ i ] ch[1][i] ch[1][i] 会有重复所以用 + + ++ ++ 而非 = 1 =1 =1):
d p [ 1 ] [ c h [ 1 ] [ i ] ] [ 0 ] + + ( 1 ≤ i < n [ 1 ] , m k [ c h [ 1 ] [ i ] ] ! = 1 ) dp[1][ch[1][i]][0]++(1\le i<n[1],mk[ch[1][i]]!=1) dp[1][ch[1][i]][0]++(1≤i<n[1],mk[ch[1][i]]!=1)
然后如果上式 i i i 取 n [ 1 ] n[1] n[1],那么这个字符串的下一位就会受到 n [ 2 ] n[2] n[2] 大小的限制,所以有:
d p [ 1 ] [ c h [ 1 ] [ n [ 1 ] ] ] [ 1 ] + + ( m k [ c h [ 1 ] [ n [ 1 ] ] ] ! = 1 ) dp[1][ch[1][n[1]]][1]++(mk[ch[1][n[1]]]!=1) dp[1][ch[1][n[1]]][1]++(mk[ch[1][n[1]]]!=1)
综上,有代码:
for(int i=1;i<=w[1]-'0';i++)
if(!mk[ch[1][i]])//不能选到不幸运的子串
(f[1][ch[1][i]][i==w[1]-'0']+=1)%=mod; //Orz
为了避免算上首位为 0 0 0 的文本串,上面的代码没有 d p [ 1