AC自动机

//HDU2222
#include<iostream> 
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std ;

const int maxn = 1e7 + 5 ;


//匹配字符串的个数 
int cnt ;  
//字典树树节点 
struct node{ 
    //字典树下一层节点 
    node * next[26] ;
    //与字典树当前节点代表的字符相同的字符的位置,该位置是树的前缀和字符串该位置的后缀的最大匹配 
    node * fail ;
    //该节点是否是单词的结尾以及单词的个数,比如ab、ab、abc,b位sum =2
    int sum ; 
};
//根节点 
node * root ;
node * newnode ;
//BFS数组 
node * q[maxn] ;
int head , tail ;
int N ;

//模式串和匹配串 
char key[70] ;
char pattern[maxn] ;

//构建 字典树|Trie树|前缀树|单词查找树
void insert( char * s){
    node * p = root ;
    for(int i = 0 ;s[i] ;i ++){
        int x = s[i] - 'a' ;
        if( p->next[x] == NULL){
            newnode = ( node * ) malloc( sizeof ( node ) ) ;
            for(int j = 0 ; j< 26 ;j++)
                newnode->next[j] = 0 ;
            newnode->sum = 0 ;
            newnode->fail = 0 ;
            p->next[x] = newnode ;
        }
        p = p->next[x] ;
    }
    p->sum ++ ;
} 
//构建 失配指针fail指针 
void build(){
    //使用BFS
    head = 0 ;
    tail =1 ;
    q[head] = root ;
    node * p ;
    node * temp ;
    while( head < tail ){
        temp = q[head ++] ;
        for( int i = 0 ; i<= 25 ;i++){
            //如果temp位的后一位存在 
            if( temp->next[i]){
                //如果temp为根节点,他的fail指向自己 
                if( temp == root)
                    temp->next[i]->fail = root ;
                else{
                    //如果为中间节点 , p指向和temp位字符相同的位置 
                    p = temp->fail ; 
                    while( p ){
                        //用i表示一个字符 
                        //temp后面的字符为i的字符的fail指针指向p位置的后面的字符为i的位置,后面字符相同 
                        if( p->next[i]){
                            temp->next[i]->fail = p->next[i] ;
                            break ;
                        }
                        p = p->fail ;
                    }
                    if( p == NULL) temp->next[i]->fail = root ; 
                }
                //BFS扩展temp的子节点 
                q[tail ++] = temp->next[i] ;
            }
        }
    } 
}
//多模匹配
void ACAT( char * ch){
    node * p = root ;
    int len = strlen( ch ) ;
    for( int i = 0 ; i< len ;i++){
        int x = ch[i] - 'a' ;
        //对于字符x,在字典树里找到和他匹配的位置 
        while( !p->next[x] && p!= root) p= p->fail ;
        p = p->next[x] ;
        //如果没有匹配的,就从根节点开始匹配
        if( !p ) p = root ;
        node* temp = p ;
        while ( temp != root) {
            //如果temp是叶节点,则表示匹配了一次模式串 
            if( temp -> sum >= 0 ){
                cnt += temp->sum ;
                //temp->sum = -1改变了字典树的结构,但是是为了防止在匹配串里面再次匹配造成重复计算 
                temp->sum = -1 ;
            }
            else break ;
            //对于abc 和bc两个模式串,他们的结尾字符相同,是两个不同的字符串,所以需要丢弃a字符,用temp= temp->fail 匹配bc字符串 
            temp = temp->fail ;
        }
    }
} 

int main(){
    int T ;
    scanf("%d" , & T) ;
    while( T--){
        root = ( node * ) malloc( sizeof( node) ) ;
        for ( int j = 0 ; j< 26 ;j++)
            root->next[j] = 0 ;
        root->fail = 0 ;
        root->sum = 0 ;
        scanf("%d" , &N) ;
        getchar() ;
        for(int i = 1 ;i<= N ; i++){
            gets(key) ;
            insert(key) ;
        }
        gets(pattern) ;
        cnt = 0 ;
        build() ;
        ACAT(pattern) ;
        printf("%d\n",cnt) ;
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值