SPOJ - LCS2 后缀自动机

本文介绍了一种寻找多个字符串中最长公共子串的有效算法。通过构建后缀自动机并运用两种处理策略,实现对输入字符串集的高效处理。第一种策略通过记录状态的最大匹配长度来确定公共子串;第二种策略则通过标记和拆分状态来识别所有公共子串。
摘要由CSDN通过智能技术生成

A string is finite sequence of characters over a non-empty finite set Σ.

In this problem, Σ is the set of lowercase letters.

Substring, also called factor, is a consecutive sequence of characters occurrences at least once in a string.

Now your task is a bit harder, for some given strings, find the length of the longest common substring of them.

Here common substring means a substring of two or more strings.

Input
The input contains at most 10 lines, each line consists of no more than 100000 lowercase letters, representing a string.

Output
The length of the longest common substring. If such string doesn’t exist, print “0” instead.

Example
Input:
alsdfkjfjkdsal
fdjskalajfkdsla
aaaajfaaaa

Output:
2
Notice: new testcases added

题意:找出n个串中最长公共子串。
做法:对一个串建后缀自动机,剩余的串在上面跑,把跑过的点标记一下,没有跑的点删除,那么剩下的自动机中的状态就是所有的公共子串,有两种处理方式.
1:因为做到一个状态x,当前最长后缀为l,说明这个状态里后缀小于等于l的子串都是符合的,那么对于每个状态保存一个cnt,代表当前状态最大可以匹配的长度,然后对所有的节点的cnt求最大就是答案了,
2:因为如果一个节点在跑了一个字符串之后没有被走过,那么这个点就可以删除,但是当走到一个状态的时候可能这个状态里面的子串只有一部分是符合的,那么就要把这个状态拆开,然后把这个状态和他的所有祖先标记,那么跑完字符串之后,得到的有标记得点就是当前所有的公共子串了。

做法一:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 3e5;
char st[N];
struct SAM{
    int pre[2*N],nex[2*N][26],mx[2*N],last,top,ans;
    int mn[2*N];
    int mp[2*N];
    void init(){
        ans = 0;
        top = 2;
        last = 1;
        memset(nex[1],0,sizeof(nex[1]));
    }
    int newnode(int x){
        mx[top] = x;
        mn[top] = x;
        for(int i = 0;i < 26;i ++) nex[top][i] = 0;
        return top ++;
    }
    void add(int x){
        int np = newnode(mx[last]+1);
        int p = last;
        while(p && nex[p][x] == 0) {
            nex[p][x] = np;
            p = pre[p];
        }
        if(!p){
            pre[np] = 1;
        }
        else{
            int q = nex[p][x];
            if(mx[q] == mx[p]+1){
                pre[np] = q;
            }
            else {
                int nq = newnode(mx[p]+1);
                for(int i = 0;i < 26;i ++) nex[nq][i] = nex[q][i];
                pre[nq] = pre[q];
                pre[np] = pre[q] = nq;
                while(p && nex[p][x] == q){
                    nex[p][x] = nq;
                    p = pre[p];
                }
            }
        }
        last = np;
    }
    void solve(char* st){
        memset(mp,0,sizeof(mp));
        int len = strlen(st);
        int p = 1;
        int nl = 0;
        for(int i = 0;i < len;i ++){
            if(nex[p][st[i]-'a']){
                nl ++;
                p = nex[p][st[i]-'a'];
                mp[p] = max(mp[p],nl);
            }
            else{
                while(p && nex[p][st[i]-'a']==0){
                    p = pre[p];
                }
                if(p == 0) {
                    p = 1,nl = 0;
                }
                else{
                    int q= nex[p][st[i]-'a'];
                    nl= mx[p] +1;
                    mp[q] = max(mp[q],nl);
                    p = q;
                }
            }
            int ps = pre[p];
            while(ps && mp[ps] != mx[ps]) mp[ps] = mx[ps],ps = pre[ps];
            //cout << i << ' '<<nl <<' '<<st[i]<< ' '<< mp[p] << endl;
        }
        ans = 0;
        for(int i = 0;i < top ;i ++) {
            mn[i] = min(mn[i],mp[i]);
            ans = max(ans,mn[i]);
        }
    }
}sa;
int main(){
    sa.init();
    scanf("%s",st);
    int len = strlen(st);
    for(int i = 0;i < len;i ++) sa.add(st[i]-'a');
    while(scanf("%s",st)!= -1){
        sa.solve(st);
    }
    printf("%d\n",sa.ans);
    return 0;
}

做法二

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 3e5;
char st[N];
struct SAM{
    int pre[12*N],nex[12*N][26],mx[12*N],last,top,ans;
    bool vis[12*N];
    void init(){
        ans = 0;
        top = 2;
        last = 1;
        memset(nex[1],0,sizeof(nex[1]));
    }
    int newnode(int x){
        mx[top] = x;
        for(int i = 0;i < 26;i ++) nex[top][i] = 0;
        return top ++;
    }
    void add(int x){
        int np = newnode(mx[last]+1);
        int p = last;
        while(p && nex[p][x] == 0) {
            nex[p][x] = np;
            p = pre[p];
        }
        if(!p){
            pre[np] = 1;
        }
        else{
            int q = nex[p][x];
            if(mx[q] == mx[p]+1){
                pre[np] = q;
            }
            else {
                int nq = newnode(mx[p]+1);
                for(int i = 0;i < 26;i ++) nex[nq][i] = nex[q][i];
                pre[nq] = pre[q];
                pre[np] = pre[q] = nq;
                while(p && nex[p][x] == q){
                    nex[p][x] = nq;
                    p = pre[p];
                }
            }
        }
        last = np;
    }
    void solve(char* st){
        memset(vis,false,sizeof(vis));
        int len =strlen(st);
        int p = 1;
        vis[p] = true;
        for(int i= 0;i < len;i ++){
            while(p && nex[p][st[i]-'a'] == 0) p = pre[p];
            if(p == 0){
                p = 1;
            }
            else{
                int q = nex[p][st[i]-'a'];
                if(mx[q] == mx[p]+1){
                    p = q;
                }
                else {
                    int nq = newnode(mx[p]+1);
                    for(int i = 0;i < 26;i ++) nex[nq][i] = nex[q][i];
                    pre[nq] = pre[q];
                    pre[q] = nq;
                    while(p && nex[p][st[i]-'a']== q){
                        nex[p][st[i]-'a'] = nq;
                        p = pre[p];
                    }
                    p = nq;
                }
            }
            int ps = p;
            while(ps && vis[ps] == false) {vis[ps] = true;ps = pre[ps];}
        }
        ans = 0;
        for(int i = 1;i < top;i ++){
            for(int j = 0;j < 26;j ++){
                if(vis[nex[i][j]] == false) nex[i][j] = 0;
            }
            if(vis[i]) ans = max(ans,mx[i]);
        }
    }
}sa;
int main(){
    sa.init();
    scanf("%s",st);
    int len = strlen(st);
    for(int i = 0;i < len;i ++) sa.add(st[i]-'a');
    while(scanf("%s",st)!= -1){
        sa.solve(st);
    }
    printf("%d\n",sa.ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值