更好的閱讀體驗:Press Here
Probelm
題目大意:
給定 個字符串 ,求其最長公共子串的長度
Solution
看起來很難,實際上非常暴力的題
設最長公共子串長度爲 ,那麼顯然最後一個串的某個長度爲 的子串能與前 個串匹配
而 SAM 在匹配時能夠求出每個位置作爲結束位置時匹配的最大長度
所以將前 個串分別建出 SAM ,用第 個串直接匹配,同時記錄每個位置作爲結尾位置與模板串匹配的最大長度
找出匹配長度最大的位置作爲答案即可
代碼如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
struct SAM {
int ch[N << 1][26] , fa[N << 1];
int siz[N << 1] , l[N << 1];
int cnt , last , len;
int ans[N];
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;
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[i] = lenth;
}
}
}sam[11];
int get(int pos , int cnt) {
int ans = N;
for(int i = 1 ; i < cnt ; ++ i) ans = min(ans , sam[i].ans[pos]);
return ans;
}
char s[N] , str[N];
int cnt = 0;
int main() {
while(~scanf("%s" , s)) {
if(cnt) sam[cnt].insert(str); ++ cnt;
swap(s , str);
}
for(int i = 1 ; i < cnt ; ++ i)
sam[i].work(str);
int n = strlen(str);
int longest = 0;
for(int i = 0 ; i < n ; ++ i)
longest = max(longest , get(i , cnt));
printf("%d\n" , longest);
}