4.【ac自动机】模式串匹配

ANSI编码的中英文16叉模式串匹配自动机

1.构造模式串树

void insert(char* s, in* trie) {

    long u = 1, len = strlen(s);//每来一个模式串
    for (long i = 0; i < len * 2; i++) {
        if (i % 2 == 0) {
            uint8_t vv = (uint8_t)s[i / 2];
            uint8_t v = vv >> 4;
            if (!trie[u].son[v])
                trie[u].son[v] = ++cnt;
            u = trie[u].son[v];
        }
        else {
            uint8_t vv = ((uint8_t)s[i / 2] << 4);
            uint8_t v = vv >> 4;//s[i]后四位
            if (!trie[u].son[v])
                trie[u].son[v] = ++cnt;
            u = trie[u].son[v];
        }
    }
    
    trie[u].patternNum++;
    if (strcmp(trie[u].strInfo, s)) {
        strcat(trie[u].strInfo, s);  if (u % 100000 == 0)printf("建树中... %d\n", u);  //不可使用指针!
    }
    
}

2.得到fail指针

void getFail(in* trie) {
    long u, y;
    for (long i = 0; i < 16; i++) {
        trie[0].son[i] = 1;          //初始化0的所有儿子都是1 所有的树型有限自动机都有一个共同的特点,即对任何输入字符a, 都有g(0, a) != 0。

    }
    queue_push(&q, 1); trie[1].fail = 0;               //将根压入队列
    while (!queue_empty(&q)) {
        queue_front(&q, &u); queue_pop(&q, &y);
        for (long i = 0; i < 16; i++) {              //遍历所有儿子
            long v = trie[u].son[i];           //处理u的i儿子的fail,这样就可以不用记父亲了
            long Fail = trie[u].fail;          //就是fafail,trie[Fail].son[i]就是和v值相同的点
            if (!v) { trie[u].son[i] = trie[Fail].son[i]; continue; }  //不存在该节点,第二种情况
            trie[v].fail = trie[Fail].son[i]; //第三种情况,直接指就可以了
            //printf("%d    %d\n", v, trie[Fail].son[i]);
            queue_push(&q, v);                      //存在实节点才压入队列
        }
    }
}

3.主串查询(不回溯)

long query(char* s, long len, in* trie) {

    long u = 1;
    for (long i = 0; i < len * 2; i++) {
        long k = 0;
        if (i % 2 == 0) {
            uint8_t vv = (uint8_t)s[i / 2];
            uint8_t v = vv >> 4;
            k = trie[u].son[v];
            while (k > 1) {
                count++;
                if (count % 10000000 == 0) printf("查询中...%lld\n", count/1000);
                if (trie[k].strInfo[0] !='\0') {
                    trie[k].flag++;
                }
                k = trie[k].fail;
            }
            u = trie[u].son[v];

        }
        else {
            uint8_t vv = ((uint8_t)s[i / 2] << 4);
            uint8_t v = vv >> 4;
            k = trie[u].son[v];
            while (k > 1) {
                count++;
                if (count % 10000000 == 0) printf("查询中...%lld\n", count/1000);
                if (trie[k].strInfo[0] != '\0') {
                    trie[k].flag++;
                }
                k = trie[k].fail;   //继续跳Fail
            }
            u = trie[u].son[v];//到下一个儿子
        }
    }
    return count;
}

4.堆排

void HeapAdjust(txt* data, int s, int m)//s=3,m=7 s=2 s是广义上的根  (下一根 ) 
{
    txt temp; int  i;
    temp = data[s];//a[3]    
    for (i = 2 * s + 1; i <= m; i = i * 2 + 1)//i=7;i<=7; i=5  i为根s的子树结点 
    {  //printf("k%d",a[i+1]);
        if (i<m && data[i].appearNum>data[i + 1].appearNum)        //i为小子树 
            i++;
        if (!(temp.appearNum > data[i].appearNum))            //初始根根和小子树比较,根小于等于小子树,退出 ; 这个temp根是初始根,并非下一根 子树可能是儿子或孙子... 
            break;
        data[s] = data[i];//初始根大于 小子树值 (这里并不一定是父子比较,第二次可能是爷孙比较)s广义根变小子树值
        s = i;//继续判断下一根 直到叶子结点 
    }
    data[s] = temp;//叶子结点值变初始根值 
}
void HeapSort(txt* data, int size)
{
    int i, j;
    txt temp;
    for (i = (size - 1) / 2; i >= 0; i--)//i=(8-1)/2=3
        HeapAdjust(data, i, size - 1);//a,3,7 a,2,7  a,1,7 a,0,7 循环调整 建立小根堆 
    for (i = size - 1; i > 0; i--)
    {
        temp = data[0];
        data[0] = data[i];
        data[i] = temp;
        HeapAdjust(data, 0, i - 1);

    }

}

5.问题:中文2字节编码后,拆成4个16进制,会出现杂糅问题。例如你好(C4E3BAC3)可能识别出愫(E3BA),模式串越短错误率越高,尤其是单字母的英文模式串。但词组错误率几乎为零。256叉树可解决此问题

posted @ 2019-11-29 14:16  阿破  阅读(111)  评论(0编辑  收藏  举报