天天看點

SPOJ LCS2 Longest Common Substring II 字尾自動機

題目大意:

就是現在給出最多10個長度不超過10^5的字元串, 求這10個串的最長公共字串的長度

大緻思路:

這是SPOJ LCS那題的更新版, 那道題我是将所有串連接配接起來中間用沒有出現過的字元隔開然後整體建立字尾自動機然後對于每個點上狀壓記錄Right集合當中元素的來源來做的

但是這個題這麼做會TLE, 雖然複雜度也是O(n)的, 但是這個題的時間限制很緊

是以後來換了一個方法, 對于輸入的第一個串建立字尾自動機, 然後将接下來輸入的所有串在這個自動機上進行類似在AC自動機上的比對, 記錄每個狀态能向前比對到的最大長度, 然後對于輸入的所有串在某一個狀态的最大比對長度取最小值就是這個狀态能夠向前找到的最長公共字串, 然後周遊一遍所有狀态即可得到所有狀态向前能得到的最長公共字串的最長的一個, 複雜度依舊是O(n)但是在周遊自動機的時候自動機的規模比剛開始想的那個方法規模小得多, 這次終于沒有TLE了...

代碼如下:

Result  :  Accepted     Memory  :  29696 KB     Time  :  160 ms

/*
 * Author: Gatevin
 * Created Time:  2015/4/9 9:24:45
 * File Name: Rin_Tohsaka.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e)
#define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endl

#define maxn 100010*2
#define maxm 100010
const int inf = 0x3f3f3f3f;

struct Suffix_Automation
{
    struct State
    {
        State *par;
        State *go[26];
        int val, mi, cnt, right;
        int maxMatch;//記錄目前能夠比對到的最大長度
        int allMatch;//記錄所有串的最多比對長度
        void init(int _val = 0)
        {
            par = 0, val = _val, cnt = 0, mi = 0, right = 0, maxMatch = 0, allMatch = inf;
            memset(go, 0, sizeof(go));
        }
        int clac()
        {
            if(par == 0) return 0;
            return val - par->val;
        }
    };
    State *root, *last, *cur;
    State nodePool[maxn];
    State* newState(int val = 0)
    {
        cur->init(val);
        return cur++;
    }
    void initSAM()
    {
        cur = nodePool;
        root = newState();
        last = root;
    }
    void extend(int w)
    {
        State *p = last;
        State *np = newState(p->val + 1);
        np->right = 1;
        while(p && p->go[w] == 0)
        {
            p->go[w] = np;
            p = p->par;
        }
        if(p == 0)
        {
            np->par = root;
        }
        else
        {
            State *q = p->go[w];
            if(p->val + 1 == q->val)
            {
                np->par = q;
            }
            else
            {
                State *nq = newState(p->val + 1);
                memcpy(nq->go, q->go, sizeof(q->go));
                nq->par = q->par;
                q->par = nq;
                np->par = nq;
                while(p && p->go[w] == q)
                {
                    p->go[w] = nq;
                    p = p->par;
                }
            }
        }
        last = np;
    }
    
    int d[maxm];
    State *b[maxn];
    void topo()
    {
        int cnt = cur - nodePool;
        memset(d, 0, sizeof(d));
        int maxVal = 0;
        for(int i = 1; i < cnt; i++)
        {
            maxVal = max(maxVal, nodePool[i].val);
            d[nodePool[i].val]++;
        }
        for(int i = 1; i <= maxVal; i++) d[i] += d[i - 1];
        for(int i = 1; i < cnt; i++) b[d[nodePool[i].val]--] = &nodePool[i];
        b[0] = root;
        return;
    }
    
    void SAMInfo()
    {
        State *p;
        int cnt = cur - nodePool;
        for(int i = cnt - 1; i > 0; i--)
        {
            p = b[i];
            p->par->right += p->right;
            p->mi = p->par->val + 1;
        }
        return;
    }
    /*
     * 對于第一個串建立好SAM之後
     * 将其他輸入的串在SAM上比對一遍, 和AC自動機上的比對相似
     * 每次向下沿着目前的字元走, 更新走到的字元的最大比對長度
     * 然後如果目前位置沒有字元w對應的邊, 就沿着parent指針向上回溯
     * 于是這裡的Parent指針就如同AC自動機中的fail指針一樣
     * 對于找打的Parent指針, 原本比對的長度就是其max值
     */
    void solve(char *s, int n)
    {
        State *now = root;
        int cnt = 0;
        for(int i = 0; i < n; i++)
        {
            int w = s[i] - 'a';
            if(now->go[w])
            {
                now = now->go[w];
                cnt++;
                now->maxMatch = max(now->maxMatch, cnt);
            }
            else
            {
                while(now && now->go[w] == 0)
                {
                    now = now->par;
                    if(now != 0)
                        now->maxMatch = max(now->maxMatch, now->val);//剛開始這裡弄成cnt了...WA了一發
                }
                if(now == 0)
                    cnt = 0, now = root;
                else
                {
                    cnt = now->val + 1;
                    now = now->go[w];
                    now->maxMatch = max(now->maxMatch, cnt);
                }
            }
        }
        cnt = cur - nodePool;
        for(int i = cnt - 1; i >= 0; i--)
        {
            b[i]->allMatch = min(b[i]->allMatch, b[i]->maxMatch);
            b[i]->maxMatch = 0;
        }
    }
};

Suffix_Automation sam;
char s[maxm];

int main()
{
    sam.initSAM();
    scanf("%s", s);
    int len = strlen(s);
    for(int i = 0; i < len; i++)
        sam.extend(s[i] - 'a');
    sam.topo();
    sam.SAMInfo();
    int n;
    while(scanf("%s", s) != EOF)
        n = strlen(s), sam.solve(s, n);
    int ans = 0;
    for(int i = 0; i < sam.cur - sam.nodePool; i++)
        ans = max(ans, sam.b[i]->allMatch);
    printf("%d\n", ans);
    return 0;
}