poj 4052 AC自动机

题意:给定2500个模式串,和长度为5,100,000的母串,问母串中包含多少个模式串。如果模式串s1是模式串s2的子串,而且s2被母串包含,则s1应被忽略。

思路:首先比较常规,对模式串建立AC自动机。然后用母串S在trie图上遍历,如果:

1、走到未被忽略的终止节点x,则将x标记为已经匹配,并且忽略掉所有目前匹配串的所有子串。 

2、走到危险但非终止的节点y, 则沿着y的前缀指针链找到第一个终止节点x,将x标记为已经匹配,并且忽略掉所有目前匹配串的所有子串。 

忽略的方法就是将x的所有前缀结点flag置为0,并在trie图中x到root之间的所有结点都如此处理一遍(相当于遍历当前串的所有子串)

最后统计匹配且未被忽略的模式串数目。代码中flag数组为1表示终止结点,为2表示危险但是非终止结点,为3表示已经匹配的结点。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 5100005
int T,n,top,len;
int t[N][26],fail[N],flag[N],f[N];
char s[N];
int newnode(){
    top++;
    for(int i = 0;i<26;i++)
        t[top][i] = -1;
    return top;
}
void init(){
    clc(fail,0);
    clc(flag,0);
    clc(f,0);
    for(int i = 0;i<26;i++)
        t[0][i] = 1;
    f[1] = 0;
    top = 0;
    newnode();
}
void input(){
    char ch;
    int i;
    len = 0;
    while((ch=getchar()) && ch!='\n'){
        if(ch=='['){
            scanf("%d",&i);
            ch = getchar();
            while(i--)
                s[len++] = ch;
            getchar();
        }else
            s[len++] = ch;
    }
    s[len] = '\0';
}
void insert(char* s){
    int i,r = 1;
    for(i = 0;s[i];i++){
        if(t[r][s[i]-'A'] == -1){
            t[r][s[i]-'A'] = newnode();
            f[top] = r;
        }
        r = t[r][s[i]-'A'];
    }
    flag[r] = 1;//为1表示是终止结点
}
void buildDFA(){
    int i,now;
    queue<int> q;
    q.push(1);
    while(!q.empty()){
        now = q.front();
        q.pop();
        for(i = 0;i<26;i++){
            if(t[now][i] == -1)
                t[now][i] = t[fail[now]][i];
            else{
                fail[t[now][i]] = t[fail[now]][i];
                q.push(t[now][i]);
                if(!flag[t[now][i]] && flag[t[fail[now]][i]] == 1)
                    flag[t[now][i]] = 2;//为2表示是危险结点,但是不是终止结点
            }
        }
    }
}
void update(int x){
    while(x){
        flag[x] = 0;
        x = fail[x];
    }
}
void match(int x){
    flag[x] = 3;
    update(fail[x]);
    x = f[x];
    while(x>1){
        update(x);
        x = f[x];
    }
}
void search(char* s){
    int i,tmp,r = 1;
    for(i = 0;s[i];i++){
        r = tmp = t[r][s[i]-'A'];
        if(flag[tmp] == 1)
            match(tmp);
        else if(flag[tmp] == 2){
            while(tmp && flag[tmp]==2)
                tmp = fail[tmp];
            if(flag[tmp] == 1)
                match(tmp);
        }
    }
}
int main(){
    scanf("%d",&T);
    while(T--){
        int i,res=0;
        init();
        scanf("%d",&n);
        getchar();
        for(i = 0;i<n;i++){
            input();
            insert(s);
        }
        buildDFA();
        input();
        search(s);
        for(i = 1;i<=top;i++)
            res+=(flag[i]==3);
        printf("%d\n",res);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值