1. 程式人生 > >SPOJ LCS Longest Common Substring SAM

SPOJ LCS Longest Common Substring SAM

Probelm

題目大意: 給定兩個字串 s , t ,求其最長公共子串的長度

1lenth(a),lenth(b)250000

Solution

既然要求最長公共子串,能夠保留所有子串資訊的資料結構我們使用 SAM

考慮將串 s 建出 SAM ,然後用串 t 在 SAM 上進行匹配

如何匹配?

  • 首先,設當前狀態為 x ,匹配到了第 i 個字元 c ,匹配長度為 len
  • ch[x][c]0 ,即有向 c 的轉移,則 x=ch[x][
    c],len=len+1,i=i+1
  • ch[x][c]=0 ,即沒有向 c 的轉移
    • 通過 fa 去找到能夠轉到 c 的最長字尾,即 x=fa[x],重複過程直到 ch[x][c]0 或者 x=0
    • ch[x][c]0 那麼狀態進行轉移,匹配長度 lenth=l[x]+1 ,狀態 x=ch[x][c]
    • x=0 則沒有任何子串含有 c 。所以將長度 lenth=0 ,狀態 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; }