SPOJ 1811(LCS) 后缀自动机

模板题。

给出两个串S, T, 求最长公共子串。|S|, |T| <= 1e5. 常规nlogn,后缀自动机O(n).

后缀自动机看了1天多了,还是晕半死,难以理解的构造原理,看了半天终于大概懂了如何构造的,嗯,拍了个模板,用一个字符串建立后缀自动机。那么…建好了之后能干嘛呢?一片空白。。又看了半天终于才懂了如何求LCS的。总结就是对A建后缀自动机,然后用B去匹配,若能匹配上就转移到儿子,否则沿着parent树向上跳。

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int maxn = (250000+5)<<1;
string s;
struct SAM{
    int son[maxn][26], pre[maxn], step[maxn],last, tot;
    void init(){
        last = tot = 0;
        memset(son[0], -1, sizeof son[0]);
        pre[0] = -1; step[0] = 0;
    }
    void extend(int c){
        int p = last, np = ++tot;
        step[np] = step[p] + 1;
        memset(son[np], -1, sizeof son[np]);
        while(p!=-1 && son[p][c] == -1) son[p][c] = np, p = pre[p];
        if(p == -1) pre[np] = 0;
        else{
            int q = son[p][c];
            if(step[q] != step[p] + 1){
                int nq = ++tot;
                memcpy(son[nq], son[q], sizeof son[q]);
                step[nq] = step[p] + 1;
                pre[nq] = pre[q];
                pre[q] = pre[np] = nq;
                while(p!=-1 && son[p][c] == q) son[p][c] = nq, p = pre[p];
            }
            else pre[np] = q;
        }
        last = np;
    }
    void build(string s)
    {
        init();
        for(int i = 0; i < s.size(); i++) extend(s[i] - 'a');
    }
     int lcs_find(const string &s)
     {
        int sz = s.size(), u = 0, tmp = 0, c, res = 0;
        for(int i = 0; i < sz; i++)
        {
            c = s[i] - 'a';
            if(son[u][c]!=-1) tmp++, u = son[u][c];
            else
            {
                while(u!=-1 && son[u][c] == -1) u = pre[u];
                if(u!=-1) tmp = step[u] + 1, u = son[u][c];
                else tmp = 0, u = 0;
            }
            res = max(res, tmp);
        }
        return res;
    }
    void debug()
    {
        for(int i = 0; i <= tot; ++i)
        {
            printf("id=%d, fa=%d, step=%d, son=[ ", i, pre[i], step[i]);
            for(int j = 0; j < 26; ++j)
            {
                if(son[i][j]!=-1)
                printf("%c,%d ", j+'a',son[i][j]);
            }
            puts("]");
        }
}
} sam;
int main()
{
        ios::sync_with_stdio(false); cin.tie(0);
       cin>>s;
        sam.build(s);
      //  sam.debug();
       cin >> s;
       cout << sam.lcs_find(s) << endl;
       return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值