当前位置 : 主页 > 大数据 > 区块链 >

@spoj - [email protected] Longest Common Substring

来源:互联网 收集:自由互联 发布时间:2021-06-22
目录 @ [emailprotected] @ [emailprotected] @accepted [emailprotected] @ [emailprotected] @[emailprotected] 求两个仅由小写字母构成的字符串的最长公共子串。 input 两行。每行一个不超过 250000 个小写字母的字

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

求两个仅由小写字母构成的字符串的最长公共子串。

input
两行。每行一个不超过 250000 个小写字母的字符串。

output
最长公共子串长度。如果没有,输出 0。

sample input
alsdfkjfjkdsal
fdjskalajfkdsla
sample output
3

@[email protected]

其实有一个不涉及任何字符串算法的简单算法。我们发现公共子串的长度是单调的(即大公共串的子串也是公共串),因此我们可以对第二个串进行滑动窗口。只是这个算法需要判断一个串是否为另一个串的子串,所以很慢。
但是这个算法可以帮助我们理解后缀自动机的过程。

后缀自动机中,我们将所有结点都设置为可接受的结点,那么可接受的串就是所有的子串。
因此我们可以将第二个串放到第一个串建成的后缀自动机上跑。如果跑不动了就沿着 fa 跳。
可以发现这个过程其实跟滑动窗口特别像。跑后缀自动机就是移动右端点,沿着 fa 跳就是移动左端点。

@accepted [email protected]

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 250000;
struct sam{
    sam *ch[26], *fa; int mx;
}pl[2*MAXN + 5], *tcnt, *root, *lst;
void init() {
    lst = tcnt = root = &pl[0];
    for(int i=0;i<26;i++)
        root->ch[i] = NULL;
    root->fa = NULL, root->mx = 0;
}
sam *newnode() {
    tcnt++;
    for(int i=0;i<26;i++)
        tcnt->ch[i] = NULL;
    tcnt->fa = NULL, tcnt->mx = 0;
    return tcnt;
}
void sam_extend(int x) {
    sam *cur = newnode(), *p = lst;
    cur->mx = lst->mx + 1; lst = cur;
    while( p && !p->ch[x] )
        p->ch[x] = cur, p = p->fa;
    if( !p )
        cur->fa = root;
    else {
        sam *q = p->ch[x];
        if( q->mx == p->mx + 1 )
            cur->fa = q;
        else {
            sam *cne = newnode();
            (*cne) = (*q); cne->mx = p->mx + 1;
            q->fa = cur->fa = cne;
            while( p && p->ch[x] == q )
                p->ch[x] = cne, p = p->fa;
        }
    }
}
char S[MAXN + 5], T[MAXN + 5];
int main() {
    init();
    scanf("%s%s", S, T);
    int lenS = strlen(S), lenT = strlen(T);
    for(int i=0;i<lenS;i++)
        sam_extend(S[i] - ‘a‘);
    int ans = 0, res = 0; sam *nw = root;
    for(int i=0;i<lenT;i++) {
        while( nw && !nw->ch[T[i] - ‘a‘] )
            nw = nw->fa;
        if( !nw ) nw = root;
        else res = min(res, nw->mx), nw = nw->ch[T[i] - ‘a‘], res++;
        ans = max(ans, res);
    }
    printf("%d\n", ans);
}

@[email protected]

注意一下每次去更新答案的不是 nw->mx 而是我们中途一直维护的一个变量 res,至于这个变量的意义可以像我说的一样用滑动窗口的方式去理解。

网友评论