POJ 3450 Corporate Identity( AC自动机 )

题意:

        给出 N 个串,问这几个串的最长公共字串是什么?若有多个输出字典序最小的。

思路:

        由于 N 小于 4000 且长度小于 200。我们完全可以用暴力的方法。

        不妨将第一个串当成母串,枚举出他的所有子串。并将其所有子串作为模式串,与其他的串进行匹配。

        这里匹配的方法直接利用了 AC自动机 。

        由于要输出字典序最小的,利用 set 维护就行。

代码:

(借用了 Kuangbin 大大的 AC自动机 模板)

#include <iostream>
#include <set>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;
string buf,str;

struct Trie
{
    set <string> ansset;
    int next[500010][27],fail[500010],end[500010],st[500010],length[500010],dans[205][205],ans[205][205];
    int root,L,anslen;
    //在这里要说明一下,此模板中 trie树 的每个节点
    //都有自己的 id 。故 st 数组和 length 数组分别
    //记录了每个节点对所在的子串的开头和从子串开始
    //到这个点的长度。                                                                                   
    int newnode()                                                                                       
    {                                                                                                    
        for(int i = 0; i < 27; i++)
            next[L][i] = -1;
        end[L++] = 0;
        return L-1;
    }
    void init()
    {
        L = 0;
        root = newnode();
        ansset.clear();
        anslen = -1;
        memset(ans,0,sizeof(ans));
    }
    void insert(int k,int len)
    {
        int now = root;
        for(int i = k; i < len; i++)
        {
            if(next[now][buf[i]-'a'] == -1){
                next[now][buf[i]-'a'] = newnode();
                st[next[now][buf[i]-'a']] = k;
                length[next[now][buf[i]-'a']] = i - k;
            }
            now = next[now][buf[i]-'a'];
        }
        end[now]++;
    }
    void build()
    {
        queue<int>Q;
        fail[root] = root;
        for(int i = 0; i < 27; i++)
            if(next[root][i] == -1)
                next[root][i] = root;
            else
            {
                fail[next[root][i]] = root;
                Q.push(next[root][i]);
            }
        while( !Q.empty() )
        {
            int now = Q.front();
            Q.pop();
            for(int i = 0; i < 27; i++)
                if(next[now][i] == -1)
                    next[now][i] = next[fail[now]][i];
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }
    int query()//由于匹配时会出现同一个串对一个结点重复匹配的情况,
               //故利用 dans 记录一个串匹配的情况。匹配结束后,再计入总的匹配统计 ans 中。
    {
        int len = str.size();
        int now = root;
        int res = 0;
        memset(dans,0,sizeof(dans));
        for(int i = 0; i < len; i++)
        {
            now = next[now][str[i]-'a'];
            int temp = now;
            while( temp != root )
            {
                res += end[temp];
                end[temp] = 0;
                dans[st[temp]][length[temp]]=1;
                temp = fail[temp];
            }
        }
        for(int i=0;i<=200;i++)
        for(int j=0;j<=200;j++){
            ans[i][j]+=dans[i][j];
        }
        return res;
    }
    void solve(int tot){
        for(int i=0;i<=200;i++){
            for(int j=0;j<=200;j++){
                if(j<anslen) continue;
                if(ans[i][j]==tot){  //............当一个节点匹配 N - 1 次时,说明这个结点在每一个串中都出现过,即公共子串。
                    if(j<anslen) continue;//.......判断长度是否不足
                    if(j>anslen)//.................若长度更长了,要更新答案集。
                        ansset.clear();
                    ansset.insert(buf.substr(i,j+1));
                    anslen=j;
                }
            }
        }
        if(ansset.empty()) cout<<"IDENTITY LOST"<<endl;
        else cout<<*ansset.begin()<<endl;
    }
    void debug()
    {
        for(int i = 0; i < L; i++)
        {
            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
            for(int j = 0; j < 128; j++)
                printf("%2d",next[i][j]);
            printf("]\n");
        }
    }
};

Trie ac;
int main()
{
    int n;
    while(cin>>n&&n){
        cin>>buf;
        int len=buf.size();
        ac.init();
        for(int i=0;i<len;i++)//..............................添加每个子串
            ac.insert(i,len);
        ac.build();
        n--;
        int t = n;
        while(t--){
            cin>>str;
            ac.query();
        }
        ac.solve(n);
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值