[BZOJ4327]-[JSOI2012]玄武密码-AC自动机

来源:互联网 发布:linux系统入门学习 编辑:程序博客网 时间:2024/05/16 18:57

说在前面

并没有什么好说的…但是要保持格式!
看这个题比较顺眼于是就去切掉了…


题目

BZOJ4327传送门

题目大意

给出一个长串,称之为母串,再给出由很多短串组成的字典。
对于每个短串,需要处理出「该短串的前缀」在「母串」上的最大匹配长度(如果该短串被包含,那么这个长度就是短串长)
短串和长串的字符集均只有「 E 」「 S 」「 W 」「 N 」

输入输出格式

第一行有两个整数,N和M,分别表示母串的长度和短串的个数。 N1e7M1e5
第二行是一个长度为N的字符串,表示母串。
之后M行,每行有一个字符串,表示短串,短串长小于等于100。


解法

很简单的AC自动机题=w=

求一个短串是否在一个长串中出现过,可以把短串的fail处理出来跑KMP。求最大匹配前缀长度的话,在KMP里随便加个变量记录一下就ok

然而数据规模吓死人,显然是不可能一个一个KMP的= =
那么显然需要用到AC自动机(废话,字典都给出来了),建立出fail指针之后,把母串拿到AC自动机上跑一遍。每到一个节点,就给这个节点打上vis标记。凡是AC自动机上被vis过的节点,就说明这个节点所代表的前缀被包含了。同时如果一个点被vis,那么它的fail也应该有vis标记。因此跑完之后,再自底向上更新vis标记。
最后把字典里每一个短串在trie上查一下最深的被vis过的点就ok了


下面是自带大常数的代码

/**************************************************************    Problem: 4327    User: Izumihanako    Language: C++    Result: Accepted    Time:3428 ms    Memory:188740 kb****************************************************************/ #include <queue>#include <cstdio>#include <cstring>#include <algorithm>using namespace stdint N , M ;char ss[100005][105] , a[10000005] ;struct Node{    bool vis ;    char c ;    Node *ch[5] , *fail ;}*root ; void newNode( Node *&nd ){    nd = new Node() ;    for( int i = 0 ; i < 5 ; i ++ )        nd->ch[i] = NULL ;    nd->fail = NULL ; nd->vis = false ;} void ext( char *ts ,int len ){    for( int i = 0 ; i < len ; i ++ )        switch( ts[i] ){            case 'E' : ts[i] = 1 ; break ;            case 'S' : ts[i] = 2 ; break ;            case 'W' : ts[i] = 3 ; break ;            case 'N' : ts[i] = 4 ; break ;        }} void Insert( char *ts ){    int len = strlen( ts ) ;    Node *nd = root ;    for( int i = 0 ; i < len ; i ++ ){        int nxt = ts[i] ;        if( !nd->ch[nxt] ){            newNode( nd->ch[nxt] ) ;            //printf( "address :%d\n" , nd->ch[nxt] ) ;            nd->ch[nxt]->c = ts[i] ;        }        nd = nd->ch[nxt] ;    }} queue<Node*> que ;void getFail(){    que.push( root ) ;    while( !que.empty() ){        Node *u = que.front() ; que.pop() ;        for( int i = 1 ; i < 5 ; i ++ ){            if( !u->ch[i] ) continue ;            Node *p = u->fail , *v = u->ch[i] ;            while( p && !p->ch[i] ) p = p->fail ;            v->fail = ( p ? p->ch[i] : root ) ;               que.push( v ) ;        }    }    root->fail = root ;} void Run(){    Node *nd = root ; root->vis = true ;    for( int i = 0 ; i < N ; i ++ ){        int nxt = a[i] ;        //printf( "adress(%d-[%d]) nxt :%d\n" , nd , nd->c , nxt ) ;        while( nd != root && !nd->ch[nxt] ){            nd = nd->fail ;            nd->vis = true ;        }        if( nd->ch[nxt] ){            nd = nd->ch[nxt] ;            nd->vis = true ;        }    }} Node *sta[40000005] ;void bfs(){    int pt = 0 , topp = 0 ;    sta[++topp] = root ;    while( pt != topp ){        pt ++ ;        Node *u = sta[pt] ;        for( int i = 1 ; i < 5 ; i ++ )            if( u->ch[i] ) sta[++topp] = u->ch[i] ;    }    for( int i = topp ; i ; i -- ){        Node *u = sta[i] ;        if( u->vis ) u->fail->vis = true ;    }} int Query( char *ts ){    int len = strlen( ts ) , rt = 0 ;    Node *nd = root ;    for( int i = 0 ; i < len ; i ++ ){        int nxt = ts[i] ;        nd = nd->ch[nxt] ;        if( !nd->vis ) return rt ;        rt ++ ;    }    return rt ;} void solve(){    getFail() ;    Run() ;    bfs() ;    for( int i = 1 ; i <= M ; i ++ )        printf( "%d\n" , Query( ss[i] ) ) ;} int main(){    scanf( "%d%d" , &N , &M ) ;    scanf( "%s" , a ) ;    ext( a , N ) ;    newNode( root ) ; root->c = 0 ;    for( int i = 1 ; i <= M ; i ++ ){        scanf( "%s" , ss[i] ) ;        ext( ss[i] , strlen( ss[i] ) ) ;        Insert( ss[i] ) ;    }    solve() ;}
原创粉丝点击