这是一道水题

给定几个特征串和几个字符串,求每个字符串中有那几个特征码

思路:

把每个特征串放到 TRIE 树里(上),用 AC 机挂着每个串跑一遍即可。但是会出现一种情况:若 B 串为 A 串子串,则可能出现没有匹配到 B 串的情况。比如 ABCD 和 BC,如果用不加修饰的 AC 机跑,会直接把 ABCD 匹配了,而 BC 在 TRIE 树的另一个分支上,没有访问。所以有 3 个解决办法:

1. 在建树前用 strstr 确定每个串是否为另一个串的子串(O(N4)TLE)。因此改成 KMP,复杂度 O(N3),然而代码量很大。

注意到方案 1 使用的 KMP 实际上是对 TRIE 树每个树枝进行匹配,由此不难想到:

2. 在建树时递归沿着 FAIL 指针向树根寻找,如果 FAIL 指针指向的节点为一个特征串的结尾,则将此特征添加到原节点。使用 vector 可以通过此题。

3. 注意到题目时间限制比较宽松,因此可以在 AC 自动机每寻找到一个节点时递归 FAIL 指针,操作通方案 2.

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

#define CHN 129
#define MX 100000
#define MXL 201
#define MXT 10001
#define KJ 32

char vir[MXL];
char web[MXT];
char data[MX];
int flag[MX];
int fail[MX];
int son[MX][CHN];
int ans[501];
int dep[100000];
int nnum;
int root=0;

void insrt(char *str,int len,int index)
{
    int now=root;
    for(int i=0;i<len;i++)
    {
        if(son[now][str[i]-KJ])
        {
            now=son[now][str[i]-KJ];
        }
        else
        {
            nnum++;
            data[nnum]=str[i];
            son[now][str[i]-KJ]=nnum;
            now=nnum;
        }
    }
    flag[now]=index;
}

int q[MXT];
int h,t;

void getfail()
{
    int now,p;
    h=t=0;
    q[0]=root;
    while(h>=t)
    {
        now=q[t];
        for(int i=0;i<CHN;i++)
        {
            if(son[now][i])
            {
                p=fail[now];
                while(p&&!son[p][i]){p=fail[p];}
                fail[son[now][i]]=(son[p][i]==son[now][i]?root:son[p][i]);
                q[++h]=son[now][i];
            }
        }
        t++;
    }
}

void query(char *str,int len)
{
    memset(ans,0,sizeof(ans));
    int now=root;
    for(int i=0;i<len;i++)
    {
        while(son[now][str[i]-KJ]==0&&now!=0)now=fail[now];
        if(flag[now])ans[flag[now]]++;
        now=son[now][str[i]-KJ];
        for(int tmp=now;tmp>0;tmp=fail[tmp])if(flag[tmp])ans[flag[tmp]]++;
    }
}

int main()
{
    int vnum,tnum,flg,totn=0;
    scanf("%d",&vnum);
    for(int i=1; i<=vnum; i++)
    {
        scanf("%s",vir);
        insrt(vir,strlen(vir),i);
    }
    getfail();
    scanf("%d",&tnum);
    for(int i=1; i<=tnum; i++)
    {
        scanf("%s",web);
        query(web,strlen(web));
        flg=0;
        for(int k=1; k<=vnum; k++)

        {
            if(ans[k])
            {
                if(flg==0)
                {
                    flg=1;
                    printf("web %d: %d",i,k);
                }
                else printf(" %d",k);
            }
        }
        if(flg==1)
        {
            puts("");
            totn++;
        }
    }
    printf("total: %d\n",totn);
    return 0;
}

分类: 文章

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注