SPOJ LCS - Longest Common Substring

题目链接.

题解:

后缀自动机的例题。

首先要知道一个性质:
一个状态s,所能代表的子串长度是 (stepparentx,stepx]

对串A建后缀自动机。

串B在上面跑。

如果x有对应的子节点,那么直接走过去,len ++

如果当前的x状态上没有对应的子节点,那就x = parent[x],直到有对应的子节点或者出了自动机。

出了自动机,len = 0,注意把x赋回root。

没出自动机,因为x的parent所代表的子串的长度一定小于x所代表的子串的长度,所以新的长度是step[x] +1。

Code:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define mem(a) memset(a, 0, sizeof a)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

const int N = 5e5 + 5;

struct suffix_automation {
    char s[N];
    int son[N][26], pre[N], step[N], last, tot;
    void push(int v) {step[++ tot] = v;}
    void Extend(int c) {
        push(step[last] + 1);
        int p = last, np = tot;
        for(;p && !son[p][c]; p = pre[p]) son[p][c] = np;
        if(!p) pre[np] = 1; else {
            int q = son[p][c];
            if(step[q] > step[p] + 1) {
                push(step[p] + 1);
                int nq = tot;
                memcpy(son[nq],son[q],sizeof son[q]);
                pre[nq] = pre[q]; pre[q] = pre[np] = nq;
                for(; son[p][c] == q; p = pre[p]) son[p][c] = nq;
            } else pre[np] = q;
        }
        last = np;
    }
    void Build() {
        scanf("%s", s);
        tot = last = 1;
        mem(son); mem(pre); mem(step);
        for(int i = 0, E = strlen(s); i < E; i ++) Extend(s[i] - 'a');
    }
    void G() {
        scanf("%s", s); int n = strlen(s);
        int x = 1, ans = 0, len = 0;
        fo(i, 0, n - 1) {
            int c = s[i] - 'a';
            if(son[x][c]) {
                x = son[x][c];
                len ++;
            } else {
                while(x && !son[x][c]) x = pre[x];
                if(x) len = step[x] + 1, x = son[x][c]; else x = 1, len = 0;
            }
            ans = max(ans, len);
        }
        printf("%d", ans);
    }
} suf;

int main() {
    suf.Build();
    suf.G();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值