SPOJ LCS2 - Longest Common Substring II

原题链接.

题解:

变成了多个串的最长公共子串了。

还是对一个串建后缀自动机。

其它的串放上去跑。

对于每个状态求一个最小值,最后答案求一个最大值就行了。

但是注意在匹配过程中,匹配到一个状态,但是实际上有很多状态也被匹配到了,考虑这种情况一个状态的值需要传递给它的parent。

这个可以全部求出后按Turpo序来搞。

为什么只传给parent是对的呢?

后缀自动机有一个性质:
两个子串的最长公共后缀为它们对应状态的在parent树上的lca。

现在B串在A串上匹配到了一个状态,C串也匹配到了一个状态,如果A、C的每个后缀相同,这样传会在lca处相遇。

反正我是这么理解的,也许parent还有什么更加神奇的理解。

Code:

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

const int N = 2e5 + 5;

struct suffix_automation {
    char s[N];
    int tot, last, son[N][26], pre[N], step[N];
    #define push(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[p] + 1 < step[q]) {
                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() {
        tot = last = 1;
        scanf("%s", s); int n = strlen(s);
        fo(i, 0, n - 1) Extend(s[i] - 'a');
    }

    int r[N], d[N];
    void dg(int x) {
            fo(i, 1, d[0]) printf("%c", d[i] + 'a');
            printf("\n");
            fo(i, 0, 25) if(son[x][i]) {
                d[++ d[0]] = i;
                dg(son[x][i]);
                d[0] --;
            }
    }
    void Turpo() {
        fo(i, 1, tot) r[pre[i]] ++;
        fo(i, 1, tot) if(!r[i]) d[++ d[0]] = i;
        for(int st = 1, en = d[0]; st <= en; st ++)
            if(!(-- r[pre[d[st]]])) d[++ en] = pre[d[st]];
    }

    int mi[N], t[N];
    void End() {
        memset(mi, 127, sizeof(mi));
        for(; scanf("%s", s) != EOF;) {
            memset(t, 0, sizeof(t));
            int x = 1, len = 0;
            int n = strlen(s);
            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 = 0, x = 1; else len = step[x] + 1, x = son[x][c];
                }
                t[x] = max(t[x], len);
            }
            fo(i, 1, tot) if(t[d[i]])
                t[pre[d[i]]] = max(t[pre[d[i]]], step[pre[d[i]]]);
            fo(i, 1, tot) mi[i] = min(mi[i], t[i]);
        }
        int ans = 0;
        fo(i, 1, tot) ans = max(ans, mi[i]);
        printf("%d", ans);
    }
} suf;

int main() {
    suf.Build();
    suf.Turpo();
    suf.End();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值