题目分析:用AC自动机预处理出从一个状态的一个数转移到下一个数时的状态,然后就可以用数位DP解决了。现在看来这题也水水的。。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
#define rep( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define clr( a , x ) memset ( a , x , sizeof a )
const int MAXN = 2005 ;
const int mod = 1e9 + 9 ;
struct ac_automaton {
int next[MAXN][2] ;
int fail[MAXN] ;
int word[MAXN] ;
int cur ;
int root ;
int Q[MAXN] ;
int head ;
int tail ;
int newnode () {
rep ( i , 0 , 2 ) next[cur][i] = -1 ;
word[cur] = 0 ;
return cur ++ ;
}
void init () {
cur = 0 ;
root = newnode () ;
}
void insert ( char buf[] ) {
int now = root ;
for ( int i = 0 ; buf[i] ; ++ i ) {
int x = buf[i] - '0' ;
if ( next[now][x] == -1 ) next[now][x] = newnode () ;
now = next[now][x] ;
}
word[now] = 1 ;
}
void build () {
head = tail = 0 ;
fail[root] = root ;
rep ( i , 0 , 2 ) {
if ( ~next[root][i] ) {
fail[next[root][i]] = root ;
Q[tail ++] = next[root][i] ;
} else next[root][i] = root ;
}
while ( head != tail ) {
int now = Q[head ++] ;
word[now] |= word[fail[now]] ;
rep ( i , 0 , 2 ) {
if ( ~next[now][i] ) {
fail[next[now][i]] = next[fail[now]][i] ;
Q[tail ++] = next[now][i] ;
} else next[now][i] = next[fail[now]][i] ;
}
}
}
} ;
ac_automaton ac ;
int digit[MAXN] ;
char buf[MAXN] ;
int bcd[MAXN][10] ;
int dp[MAXN][MAXN] ;
int n ;
int get_next ( int now , int x ) {
if ( ac.word[now] ) return -1 ;
rev ( i , 3 , 0 ) {
if ( ac.word[ac.next[now][( x >> i ) & 1]] ) return -1 ;
now = ac.next[now][( x >> i ) & 1] ;
}
return now ;
}
int dfs ( int cur , int s , int limit , int pre_zero ) {
if ( cur == -1 ) return 1 ;
if ( !limit && dp[cur][s] != -1 ) return dp[cur][s] ;
int ans = 0 , n = limit ? digit[cur] : 9 ;
if ( pre_zero ) {
ans += dfs ( cur - 1 , s , limit && digit[cur] == 0 , 1 ) ;
if ( ans >= mod ) ans -= mod ;
} else if ( ~bcd[s][0] ) {
ans += dfs ( cur - 1 , bcd[s][0] , limit && digit[cur] == 0 , 0 ) ;
if ( ans >= mod ) ans -= mod ;
}
For ( i , 1 , n ) if ( ~bcd[s][i] ) {
ans += dfs ( cur - 1 , bcd[s][i] , limit && n == i , 0 ) ;
if ( ans >= mod ) ans -= mod ;
}
if ( !limit && !pre_zero ) dp[cur][s] = ans ;
return ans ;
}
int deal ( char buf[] , int len ) {
rep ( i , 0 , len ) digit[i] = buf[len - i - 1] - '0' ;
return dfs ( len - 1 , 0 , 1 , 1 ) ;
}
void solve () {
int ans = 0 ;
ac.init () ;
clr ( dp , -1 ) ;
scanf ( "%d" , &n ) ;
For ( i , 1 , n ) {
scanf ( "%s" , buf ) ;
ac.insert ( buf ) ;
}
ac.build () ;
rep ( i , 0 , ac.cur ) rep ( j , 0 , 10 ) bcd[i][j] = get_next ( i , j ) ;
scanf ( "%s" , buf ) ;
int len1 = strlen ( buf ) ;
rev ( i , len1 - 1 , 0 ) {
if ( buf[i] > '0' ) {
-- buf[i] ;
break ;
} else buf[i] = '9' ;
}
ans -= deal ( buf , len1 ) ;
scanf ( "%s" , buf ) ;
int len2 = strlen ( buf ) ;
ans += deal ( buf , len2 ) ;
if ( ans < 0 ) ans += mod ;
printf ( "%d\n" , ans ) ;
}
int main () {
int T ;
scanf ( "%d" , &T ) ;
while ( T -- ) solve () ;
return 0 ;
}

本文介绍了一道使用AC自动机预处理状态转移,并结合数位DP解决的问题。通过构建AC自动机来处理字符串匹配,进而利用数位DP求解特定数值范围内的合法解数量。

1552

被折叠的 条评论
为什么被折叠?



