天天看点

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;
}