SPOJ-LCS2 Longest Common Substring II (后缀自动机)

题目链接:https://www.spoj.com/problems/LCS2/en/

解题思路:

对于两个串的LCS,我们只需对其中一个建sam,另一个在上面跑,记录最大值即可。

对于多个串的话,我们需要记录每个串跑sam的时候在每个节点的相同子串取最大值,每次跑完更新一下每个节点当前所有串的最长相同子串取最小值。

需要注意的是失配时跳fa数组的时候也需要更新fa节点的数据。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<set>
#include<map>
#include<algorithm>

using namespace std;

#define ll long long
#define ull unsigned long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define pf push_front
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define dfl(x) ll x;scanf("%I64d",&x)
#define df2l(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define df(x) int x;scanf("%d",&x);
#define df2(x,y) int x,y;scanf("%d %d",&x,&y)
#define mod 1000000007
#define duozu(T) int T;scanf("%d",&T);while (T--)
//const double pi = acos(-1.0);
/*x = x*cosB-y*sinB , y = y*cosB-x*sinB*/

const int N = 1e5+5;

char s[N];

const int MAXN = 1e5+5;
const int ALP = 26;

struct SAM
{
    int trie[MAXN<<1][ALP];
    int len[MAXN<<1];
    int fa[MAXN<<1];
    int sz,las;
    int LCS[MAXN<<1];
    int nowlcs[MAXN<<1];

    void init(){
        newnode(las=sz=0);
        fa[0] = -1;
    }

    int newnode(int x){
        memset(trie[x],0,sizeof trie[x]);
        len[x] = 0;
        LCS[x] = INF;
        return sz++;
    }

    int idx(char ch){
        return ch-'a';
    }

    void add(char ch){
        int c = idx(ch);
        int p = las;
        int np = newnode(sz);
        las = np;
        len[np] = len[p]+1;
        for(;~p && !trie[p][c];p=fa[p]) trie[p][c] = np;
        if (p==-1) fa[np] = 0;
        else {
            int q = trie[p][c];
            if (len[q]==len[p]+1) fa[np] = q;
            else {
                int nq = newnode(sz);
                fa[nq] = fa[q];
                for0(i,0,ALP) trie[nq][i] = trie[q][i];
                len[nq] = len[p]+1;
                fa[np] = fa[q] = nq;
                for (;~p && trie[p][c]==q;p=fa[p]) trie[p][c] = nq;
            }
        }
    }

    void find(char *s){//跑第2~n个串
        memset(nowlcs,0,sizeof nowlcs);
        int u=0,nowlen=0;
        for (int i=0;s[i];i++){
            int c = idx(s[i]);
            while (~u && !trie[u][c]){
                u = fa[u];
                if (~u) nowlen = len[u],nowlcs[u] = max(nowlcs[u],nowlen);
            }
            if (u==-1) {nowlen = u = 0;continue;}
            u = trie[u][c];
            nowlen++;
            nowlcs[u] = max(nowlcs[u],nowlen);
        }
        for0(i,0,sz) LCS[i] = min(LCS[i],nowlcs[i]);
    }

    int solve(){//取每个节点最长子串的最大值
        int ans = 0;
        for0(i,0,sz) /*if (LCS[i]!=INF)*/ans = max(ans,LCS[i]);
        return ans;
    }
}sam;


int main()
{
    //freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
    //freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
    scanf("%s",s);
    sam.init();
    for (int i=0;s[i];i++) sam.add(s[i]);
    while (~scanf("%s",s)) sam.find(s);
    printf("%d\n",sam.solve());
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值