POJ2774 Long Long Message 【SAM】

POJ2774 Long Long Message

找两个串的最长公共字串
对其中一个串\(s\)\(SAM\),然后我们如何找到最长公共字串,办法就是枚举\(t\)串所有的前缀,然后找各个前缀的最长能和\(s\)串匹配的后缀。
如果一个个跑需要\(O(n^2)\)\(SAM\)可以来保存之前匹配的状态,假设现在匹配的状态是\(u\),匹配到的最长后缀长度为\(l\),那么现在考虑在当前状态后面加上一个字符,也就是成为\(t\)串一个新的前缀,那么最大能匹配的必然是在上一次匹配到的最长串的基础上去匹配,所以我们可以不断判断\(u\)这个状态是否有连向新加入的字符的边,如果有的话,更新\(u,l \Rightarrow u = ch[u][c]; l+=1\),如果没有的话,就要跑当前状态的后缀链接,找到最长的\(endpos\)不同的之前匹配串的一个后缀,然后更新\(u,l \Rightarrow u = link[u]; l = len[u]\),直到遇到有连边的状态或者到了初始点。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<stack>
using namespace std;
void ____(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); }
const int MAXN = 2e5+7;
char s[MAXN];
struct SAM{
    int len[MAXN],link[MAXN],ch[MAXN][26],last,tot;
    void init(){ link[tot = last = 0] = -1; memset(ch[0],0,sizeof(ch[0])); }
    void extend(int c){
        int np = ++tot; memset(ch[tot],0,sizeof(ch[tot]));
        int p = last; len[np] = len[last] + 1;
        while(p!=-1 and !ch[p][c]){
            ch[p][c] = np;
            p = link[p];
        }
        if(p==-1) link[np] = 0;
        else{
            int q = ch[p][c];
            if(len[p]+1==len[q]) link[np] = q;
            else{
                int clone = ++tot;
                len[clone] = len[p] + 1;
                link[clone] = link[q];
                for(int i = 0; i < 26; i++) ch[clone][i] = ch[q][i];
                link[np] = link[q] = clone;
                while(p!=-1 and ch[p][c]==q){
                    ch[p][c] = clone;
                    p = link[p];
                }
            }
        }
        last = np;
    }
    int lcs(char *str){
        int ret = 0, u = 0, l = 0, n = strlen(str);
        for(int i = 0; i < n; i++){
            int c = str[i] - 'a';
            while(u and !ch[u][c]){
                u = link[u];
                l = len[u];
            }
            if(ch[u][c]) u = ch[u][c], l++;
            ret = max(ret,l);
        }
        return ret;
    }
}sam;
int main(){
    while(scanf("%s",s)!=EOF){
        sam.init(); int n = strlen(s);
        for(int i = 0; i < n; i++) sam.extend(s[i]-'a');
        scanf("%s",s);
        printf("%d\n",sam.lcs(s));
    }
    return 0;
}
posted @ 2020-04-14 16:02  _kiko  阅读(99)  评论(0编辑  收藏  举报