HDU5880【AC自动机】

题意:

给出n个字符串,再给出一个字符串,把之前出现过的字符串全部变成*

思路:
AC自动机,Trie树上存的值是一个字符串的长度,也就是往前的长度,然后倒着处理一遍。

感想:

第三题AC自动机,本来就是想脱离模板多练练,虽然之前撒比bug错了一大堆,但是收获很多啊。

重要的感想有两个方面:

一:

在我们solve主串的时候:

在通过移动失败指针处理后缀串的时候,在这道题里只要找到一个就行了。

看了网上,主要有两种标记方法(其实类似),其中一种就是通过标记这个位置最长后缀来处理,这样完全可行,然而那个博主的处理方法并不合适,

那个博主的方法是在通过移动失败指针处理后缀串的时候,还在比较取这个位置的后缀串最大,其实理解的话,我们完全不需要比较啊,理由:这个后缀串本身就是你的子串,何必啊???而且在通过移动失败指针处理后缀串的时候第一个即最长。

二:

一开始无脑在线printf,本身就是比较费时的写法,然后就靠评测机抖一抖AC,直接先转变好,一发printf,妥妥的省了一堆时间。

(三:

模板还是网赛用用吧???

//#include <bits/stdc++.h>
#include<iostream>
#include<queue>
#include<string.h>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;

const int N=1e6+10;
struct Trie{
    int num;
    Trie *next[27],*fail;
};
Trie q[N],*root;
int tol;
char word[N],str[N];
//int latersum[N];//第一种方法
int flag[N];//第二种方法

Trie* Creat()
{
     Trie *p;
     p=&q[tol++];
     p->num=0;
     p->fail=NULL;
     for(int i=0;i<26;i++)
        p->next[i]=NULL;
    return p;
}

void Insert()
{
    Trie* p=root;
    int len=strlen(str),index;
    for(int i=0;i<len;i++)
    {
        index=str[i]-'a';
        if(p->next[index]==NULL)
            p->next[index]=Creat();
        p=p->next[index];
    }
    p->num=len;
}

void Build_Ac()
{
    queue<Trie*>que;
    que.push(root);
    while(!que.empty())
    {
        Trie *p=que.front();que.pop();
        for(int i=0;i<26;i++)
        {
            if(p->next[i]!=NULL)
            {
                if(p==root)
                    p->next[i]->fail=root;
                else
                {
                    Trie* temp=p->fail;
                    while(temp!=NULL)
                    {
                        if(temp->next[i]!=NULL){
                            p->next[i]->fail=temp->next[i];
                            break;
                        }
                        temp=temp->fail;
                    }
                    if(temp==NULL)
                        p->next[i]->fail=root;
                }
                que.push(p->next[i]);
            }
        }
    }
}

void Query()
{
    int len=strlen(word),index;

    Trie *p=root;
    for(int i=0;i<len;i++)
    {
//        latersum[i]=0;
        flag[i]=0;
        if(!((word[i]>='a'&& word[i]<='z')||(word[i]>='A'&&word[i]<='Z')))
                continue;
        index=(word[i]>='A'&&word[i]<='Z')?(tolower(word[i])-'a'):(word[i]-'a');
        while(p->next[index]==NULL && p!=root)
            p=p->fail;
        p=p->next[index];
        if(p==NULL)
            p=root;
        Trie *temp=p;
        while(temp!=root)
        {
            if(temp->num){
//                flag[i]=max(flag[i],temp->num);
                flag[i]=temp->num;
//                latersum[i+1]--;
//                latersum[i-temp->num+1]++;
                break;//第一个后缀一定是最长的,不需要在转移到别的fail指针
            }
            temp=temp->fail;
        }
    }
//    int nn=0;//无脑printf,此代码要看天命AC
//    for(int i=0;i<len;i++)
//    {
//        nn+=latersum[i];
////        if(nn<=0)
////            printf("%c",word[i]);
////        else
//        if(nn>0)
//            word[i]='*';
//    }
//    puts("");
    int nn=0;
    for(int i=len-1;i>=0;i--)
    {
        nn=max(nn,flag[i]);
        if(!nn) continue;
        else{
            word[i]='*';
            nn--;
        }
    }
    printf("%s\n",word);
}

int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        tol=0;
        root=Creat();
        while(n--)
        {
            scanf("%s",str);
            Insert();
        }
        getchar();
        Build_Ac();
        gets(word);
        Query();
    }
    return 0;
}




posted @ 2017-02-11 21:18  see_you_later  阅读(182)  评论(0编辑  收藏  举报