SPOJ LCS Longest Common Substring SAM

更好的阅读体验:Press Here

Probelm

传送门 >ω<

题目大意:
给定两个字符串 s s , t ,求其最长公共子串的长度

1lenth(a),lenth(b)250000 1 ≤ l e n t h ( a ) , l e n t h ( b ) ≤ 250000

Solution

既然要求最长公共子串,能够保留所有子串信息的数据结构我们使用 SAM

考虑将串 s s 建出 SAM ,然后用串 t 在 SAM 上进行匹配

如何匹配?

  • 首先,设当前状态为 x x ,匹配到了第 i 个字符 c c ,匹配长度为 len
  • ch[x][c]0 c h [ x ] [ c ] ≠ 0 ,即有向 c c 的转移,则 x=ch[x][c],len=len+1,i=i+1
  • ch[x][c]=0 c h [ x ] [ c ] = 0 ,即没有向 c c 的转移
    • 通过 fa 去找到能够转到 c c 的最长后缀,即 x=fa[x],重复过程直到 ch[x][c]0 c h [ x ] [ c ] ≠ 0 或者 x=0 x = 0
    • ch[x][c]0 c h [ x ] [ c ] ≠ 0 那么状态进行转移,匹配长度 lenth=l[x]+1 l e n t h = l [ x ] + 1 ,状态 x=ch[x][c] x = c h [ x ] [ c ]
    • x=0 x = 0 则没有任何子串含有 c c 。所以将长度 lenth=0 ,状态 x=1 x = 1 (也就是根)

    同时,在匹配时维护一个匹配长度最大值即可

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 300010;
    struct SAM {
        int ch[N << 1][26] , fa[N << 1];
        int siz[N << 1] , l[N << 1];
        int cnt , last , len;
        void ins(int c) {
            int x = last , nx = ++ cnt; last = nx;
            l[nx] = l[x] + 1; siz[nx] = 1;
            for(; x && !ch[x][c] ; x = fa[x]) ch[x][c] = nx;
            if(!x) fa[nx] = 1;
            else {
                int y = ch[x][c];
                if(l[y] == l[x] + 1) fa[nx] = y;
                else {
                    int ny = ++cnt; l[ny] = l[x] + 1;
                    memcpy(ch[ny] , ch[y] , sizeof(ch[y]));
                    fa[ny] = fa[y]; fa[y] = fa[nx] = ny;
                    for(; ch[x][c] == y ; x = fa[x]) ch[x][c] = ny;
                }
            }
        }
        void insert(char *s) {
            len = strlen(s);
            last = cnt = 1;
            for(int i = 0 ; i < len ; ++ i) ins(s[i] - 'a');
        }
        void work(char *s) {
            len = strlen(s);
            int x = 1 , lenth = 0 , ans = 0;
            for(int i = 0 ; i < len ; ++ i) {
                int c = s[i] - 'a';
                if(ch[x][c]) {x = ch[x][c]; ++ lenth;}
                else {
                    while(x && !ch[x][c]) x = fa[x];
                    if(x) {lenth = l[x] + 1; x = ch[x][c];}
                    else {lenth = 0; x = 1;}
                }
                ans = max(ans , lenth);
            }
            printf("%d\n" , ans);
        }
    }sam;
    char s[N] , t[N];
    int main() {
        scanf("%s %s" , s , t);
        sam.insert(s); sam.work(t);
        return 0;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值