SPOJ LCS Longest Common Substring SAM
阿新 • • 發佈:2018-12-09
Probelm
題目大意: 給定兩個字串 , ,求其最長公共子串的長度
Solution
既然要求最長公共子串,能夠保留所有子串資訊的資料結構我們使用 SAM
考慮將串 建出 SAM ,然後用串 在 SAM 上進行匹配
如何匹配?
- 首先,設當前狀態為 ,匹配到了第 個字元 ,匹配長度為
- 若 ,即有向 的轉移,則
- 若 ,即沒有向 的轉移
- 通過 去找到能夠轉到 的最長字尾,即 ,重複過程直到 或者
- 若 那麼狀態進行轉移,匹配長度 ,狀態
- 若 則沒有任何子串含有 。所以將長度 ,狀態 (也就是根)
同時,在匹配時維護一個匹配長度最大值即可
程式碼如下:
#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;
}