poj4052 Hrinity

pdf题面:传送门

题目大意:给定一些单词和一个句子,问有多少个单词在句子中出现过,如果一个但单词包含另一个单词,并且两个单词都出现过,那么只算最外层的单词(包含另一个单词的单词).

分析:这道题如果没有第二个条件的话就和hdu2222是一模一样的题.但是没关系,可以先用hdu2222的方法找出所有出现过的单词,然后每个单词将它的子串给标记.如何找一个串的子串呢?如果一个字符串s[1......n],它的子串必定在s[1......r]和s[l......n]中,也就是在前缀和后缀中,在trie里,找前缀可以利用父亲节点.找后缀可以利用AC自动机的fail指针,这样递归地标记一下就可以了.

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 200010,maxm = 5120010;
char s[maxm],ss[maxm];
int T,n,cnt,tot = 1,ans;
bool vis[maxn],tag[maxn],vis2[maxn];

void init()
{
    n = cnt = ans = 0;
    memset(vis,false,sizeof(vis));
    memset(vis2,false,sizeof(vis2));
    memset(tag,false,sizeof(tag));
}

struct node
{
    int tr[30],fail,id,fa;
    void clear()
    {
        memset(tr,0,sizeof(tr));
        fail = id = fa = 0;
    }
} e[maxn];

void insert(int x)
{
    int u = 1;
    for (int i = 1; i <= cnt; i++)
    {
        int ch = ss[i] - 'A';
        if (!e[u].tr[ch])
        {
            e[u].tr[ch] = ++tot;
            e[tot].clear();
        }
        int temp = u;
        u = e[u].tr[ch];
        e[u].fa = temp;
    }
    e[u].id = x;
}

void build()
{
    queue <int> q;
    for (int i = 0; i < 26; i++)
        e[0].tr[i] = 1;
    q.push(1);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        int fail = e[u].fail;
        for (int i = 0; i < 26; i++)
        {
            int y = e[u].tr[i];
            if (y)
            {
                e[y].fail = e[fail].tr[i];
                q.push(y);
            }
            else
                e[u].tr[i] = e[fail].tr[i];
        }
    }
}

void dfs(int x)
{
    if (tag[x])
        return;
    tag[x] = 1;
    vis[x] = 0;
    if (e[x].fail)
    dfs(e[x].fail);
    if (e[x].fa)
    dfs(e[x].fa);
}

bool ischar(char p)
{
    return p >= 'A' && p <= 'Z';
}

void getchange()
{
    int len = strlen(s + 1);
    cnt = 0;
    for (int i = 1; i <= len; i++)
    {
        if (ischar(s[i]))
            ss[++cnt] = s[i];
        else
        {
            i++;
            int res = 0;
            while (s[i] >= '0' && s[i] <= '9')
            {
                res = res * 10 + s[i] - '0';
                i++;
            }
            char cc = s[i++];
            while (res)
            {
                ss[++cnt] = cc;
                res--;
            }
        }
    }
}

int main()
{
    scanf("%d",&T);
    while (T--)
    {
        init();
        e[tot = 1].clear();
        scanf("%d",&n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%s",s + 1);
            getchange();
            insert(i);
        }
        build();
        scanf("%s",s + 1);
        getchange();
        int u = 1;
        for (int i = 1; i <= cnt; i++)
        {
            int ch = ss[i] - 'A';
            while (u && !e[u].tr[ch])
                u = e[u].fail;
            u = e[u].tr[ch];
            int t = u;
            while (t && !vis2[t])
            {
                vis2[t] = 1;
                if (e[t].id)
                    vis[t] = 1;
                t = e[t].fail;
            }
        }

        for (int i = 1; i <= tot; i++)
            if (!tag[i] && vis[i])
            {
                dfs(e[i].fail);
                dfs(e[i].fa);
            }
        for (int i = 1; i <= tot; i++)
            if (vis[i])
                ans++;
        printf("%d\n",ans);
    }

    return 0;
}

 

posted @ 2017-12-19 15:00  zbtrs  阅读(389)  评论(0编辑  收藏  举报