SPOJ1812 LCS2
题意:给n个串,求最长公共子串
做法:对第一个串建\(SAM\),拿剩余的串类似于求\(LCS\)的在上面跑,对于当前这个串,求出可以到达每个状态的最长子串长度,然后,每个状态对每个串的匹配取最小值,最后取最大值就是答案。现在考虑如何求到达每个状态的最长子串长度,我们先类似于求\(LCS\)的,维护一个\(now\)表示当前状态,\(l\)表示匹配的长度,在跑的过程中更新每个状态的最长子串长度,显然更新完一个状态后,这个状态的所有有缀,即\(parent\)树上,这个状态的祖先们,都应该更新,于是在匹配都结束后,倒着拓扑序更新完所有的状态。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>const int N = 100010;
typedef long long ll;using namespace std;struct SAM {int n, step[N<<1], fa[N<<1], ch[N<<1][26], tmp[N<<1], num[N<<1], mn[N<<1], last, root, cnt, A[N];char s[N];void init() {memset(mn, 0x3f, sizeof(mn));cnt = 0; last = root = ++ cnt;}void add(int x) {int p = last, np = ++cnt;step[np] = step[p] + 1;while(p && !ch[p][x]) ch[p][x] = np, p = fa[p];if(!p) fa[np] = root;else {int q = ch[p][x];if(step[q] == step[p] + 1) fa[np] = q;else {int nq = ++ cnt;fa[nq] = fa[q]; memcpy(ch[nq], ch[q], sizeof(ch[q]));fa[q] = fa[np] = nq; step[nq] = step[p] + 1;while(p && ch[p][x] == q) ch[p][x] = nq, p = fa[p];}}last = np;}void calright() {for(int i = 1; i <= cnt; ++i) ++ A[step[i]];for(int i = 1; i <= n; ++i) A[i] += A[i-1];for(int i = cnt; i; --i) num[A[step[i]]--] = i;}void run() {scanf(" %s",s+1), n = strlen(s + 1);for(int i = 1; i <= n; ++i) add(s[i]-'a');calright();memset(mn, 0x3f, sizeof(mn));}void solve(char b[]) {memset(tmp,0,sizeof(tmp));int m = strlen(b+1), now = 1, l = 0;for(int i = 1; i <= m; ++i) {if(ch[now][b[i] - 'a']) ++ l, now = ch[now][b[i] - 'a'];else {while(now && !ch[now][b[i] - 'a']) now = fa[now];if(!now) now = 1, l = 0;else l = step[now]+1, now = ch[now][b[i] - 'a'];}tmp[now] = max(tmp[now], l); /// only update the end}for(int i = cnt; i; --i) {mn[num[i]] = min(mn[num[i]], tmp[num[i]]);if( fa[num[i]] && tmp[num[i]] ) tmp[fa[num[i]]] = step[fa[num[i]]];}}} Fe;char str[N];int main() {// freopen("in","r",stdin);Fe.init();Fe.run();while(~ scanf(" %s",str+1)) {Fe.solve(str);}int ans = 0;for(int i = 1; i <= Fe.cnt; ++i) ans = max(ans, Fe.mn[i]);printf("%d\n",ans);return 0;
}