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

说在前面

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


题目

BZOJ4327传送门

题目大意

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

输入输出格式

第一行有两个整数,N和M,分别表示母串的长度和短串的个数。 N1e7 M1e5
第二行是一个长度为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 
#include 
#include 
#include 
using namespace std ;
 
int 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 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() ;
}

你可能感兴趣的:(AC自动机)