当前位置:编程学习 > C/C++ >>

Zoj 3494 BCD Code (字符串_AC自动机(数位DP))

题目大意: 问A到B之间的所有整数,转换成BCD Code后,有多少个不包含属于给定病毒串集合的子串,A,B <=10^200,病毒串总长度<= 2000.

解题思路: 因为时间问题,这是我这次复习AC自动机的最后一题了。ac自动机A的很爽,可是其他方面太弱逼,无可奈何必须去复习其他方面了。
    看完题目就给出题者跪了,看似毫不相关但实则有联系的的两个东西被结合起来,真是神来之笔。AC自动机上明显的状态使得我们很容易在AC自动机上进行DP,而数位DP又是DP里面一类比较典型的DP,之前我一直认为数位DP应该是利用数字直接一位一位的构造就可以,这题利用自动机构造出一个数字与其他数字间能否相互到达的矩阵,然后利用这个矩阵进行数位DP。这样一分析,似乎并不是毫无相关。
    以终为始,我们先分析下最后的数位dp为什么需要用到自动机。如果一个数不含病毒串,那么这个数中的所有数字编成BCD码并且连接起来后就必须不包含病毒串。普通的数位DP构造的两个相邻数字没什么约束,而这题的约束有两个,一是数字本身编成BCD码后可能含有病毒串,二是两个数字连起来后他们的BCD码也连起来了,这时可能含有病毒串,一是二的特例。这样问题就转变成了我们构造若干个串,使得这些串不包含病毒串。问题一转变就和AC自动机扯上关系了,我们用这个串去ac自动机上跑一跑就知道含不含病毒串了。
    我们需要将自动机上的状态压缩成一个mat[MAXSIZE][10]矩阵,mat[i][j]表示位置i走过j编成的BCD码即四位二进制数后到达的位置,如果没办法到达设为-1.接着我们利用mat进行数位DP,mat[i][j]中的i表示位置,但除root外它也表示数字,i->next[k] = ii,这里的ii就表示k。
    用dp[pos][ac][limit][zero]来表示状态,pos表示数字的第几位,ac为ac自动机上的位置,limit=0表示没有上限,limit=1表示上限为digit[pos],zero为0表示非前导0,此时需要判断mat[ac][k]是否为-1,为-1说明没办法构造下一个数字k,否则可以构造,当zero为1表示是前导0,如果下一个是0那么表示这些都是无用的0,转移到下一个位置都应该是root,不是0的话就需要判断mat[0][k]了。
    状态明确了以后用记忆化搜索很好写。

测试数据:
Input:
10
0
1 10
1
00
1 10
1
00
1 100
1
1111
1 100
2
01
10
1 100

Output:
10
3
9
98
0

代码:
[cpp] 
#include <stdio.h> 
#include <string.h> 
#include <queue> 
#include <algorithm> 
using namespace std; 
#define MAXNODE 2 
#define MAXSIZE 2100 
#define MOD 1000000009 
 
 
int n,m; 
int ans; 
char str[MAXSIZE]; 
char A[MAXSIZE],B[MAXSIZE]; 
 
 
struct ACnode { 
 
    int flag,in; 
    ACnode *next[MAXNODE],*fail,*father; 
}; 
struct AC_Auto { 
 
    int head,tail,total; 
    int digit[210]; 
    int ans,dp[210][MAXSIZE][2][2]; 
    int next[MAXSIZE][10]; 
    ACnode *root,*p,*q; 
    ACnode *qu[MAXSIZE],tree[MAXSIZE]; 
 
 
    void Initial() { 
 
        total = 0; 
        head = tail = 0; 
        root = CreateNode(); 
        root->fail = root; 
    } 
    ACnode *CreateNode() { 
 
        tree[total].flag = 0; 
        tree[total].in = total; 
        tree[total].next[0] = NULL; 
        tree[total].next[1] = NULL; 
        return &tree[total++]; 
    } 
    int GetHash(char c) { 
 
        return c - '0'; 
    } 
    void Insert(char *str) { 
 
        p = root; 
        for (int i = 0; str[i]; ++i) { 
 
            int k = GetHash(str[i]); 
            if (p->next[k] == NULL) 
                p->next[k] = CreateNode(); 
            p = p->next[k]; 
        } 
        p->flag = 1; 
    } 
    void Build_AC() { 
 
        qu[head++] = root; 
        while (tail < head) { 
 
            p = qu[tail++]; 
            for (int k = 0; k < MAXNODE; ++k)  
                if (p->next[k]) { 
 
                    if (p == root) p->next[k]->fail = root; 
                    else p->next[k]->fail = p->fail->next[k]; 
                    p->next[k]->flag |= p->next[k]->fail->flag; 
                    qu[head++] = p->next[k]; 
                } 
                else { 
 
                    if (p == root) p->next[k] = root; 
                    else p->next[k] = p->fail->next[k]; 
                } 
        } 
    } 
    int Next(int pos,int num) { 
 
        if (tree[pos].flag == 1) return -1; 
        int realpos = pos; 
        for (int i = 3; i >= 0; --i) { 
 
            int k = ((num >> i) & 1); 
            if (tree[realpos].next[k]->flag != 1) 
                realpos = tree[realpos].next[k]->in; 
            else return -1; 
        } 
       

补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,