SPOJ LCS2
LCS2 - Longest Common Substring II
题意:
求N(N<=10)个串的最长公共子串。
分析:
poj2774上那道题,对一个串建立后缀自动机,另一个在上面匹配。
这道题是对多个串求。那么同样,让每个串在后缀自动机上匹配,然后记录在后缀自动机的每个节点上记录,当前串在这个位置和第一个串的最大匹配数,h数组。
然后mn数组,每次对于这所有的节点的h取小,为从第2个串到现在所有的串,都能在这个节点上匹配的长度。
因为一旦某个节点匹配上了,那么它的父节点(parent树)的父节点都会匹配上(因为父节点是当前点的后缀),所以按拓扑倒序,更新父节点的h,为父节点的len,(即最大长度)。
第二种写法是对n-1个字符串建立SAM,然后用最后一个串在n-1个串上匹配,每个自动机上都有一个当前的指针cur,当前答案ans。对最后一个串从头开始扫,求出最后一个串和每个串以当前字符结尾的最大匹配长度,在这里面取小,每次加入一个字符,可以直接判断cur的下一位,不需要从头开始。空间太大。
两种写法大同小异,只枚举举的顺序不同而已。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 inline int read() { 6 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 7 for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 8 } 9 10 const int N = 100010; 11 12 struct Suffix_Automaton{ 13 int fa[N<<1], trans[N<<1][26], len[N<<1], Last, Index; 14 int v[N], sa[N<<1], mn[N<<1], h[N<<1]; 15 char s[N]; 16 17 void extend(int c) { 18 int P = Last,NP = ++Index; 19 len[NP] = len[P] + 1; 20 for (; P&&!trans[P][c]; P=fa[P]) trans[P][c] = NP; 21 if (!P) fa[NP] = 1; //- 22 else { 23 int Q = trans[P][c]; 24 if (len[P] + 1 == len[Q]) fa[NP] = Q; 25 else { 26 int NQ = ++Index; 27 fa[NQ] = fa[Q]; 28 len[NQ] = len[P] + 1; 29 memcpy(trans[NQ], trans[Q], sizeof trans[Q]); 30 fa[Q] = NQ; 31 fa[NP] = NQ; 32 for (; P&&trans[P][c]==Q; P=fa[P]) trans[P][c] = NQ; 33 } 34 } 35 Last = NP; 36 } 37 void build() { 38 Last = Index = 1; 39 scanf("%s",s+1); 40 int n = strlen(s+1); 41 for (int i=1; i<=n; ++i) extend(s[i] - 'a'); 42 for (int i=1; i<=Index; ++i) v[len[i]] ++; 43 for (int i=1; i<=n; ++i) v[i] += v[i-1]; 44 for (int i=1; i<=Index; ++i) sa[ v[len[i]]-- ] = i; // sa[i] 排名为i的节点。按深度排名(拓扑用) 45 } 46 void calcc() { 47 int n = strlen(s+1), now = 0, p = 1; 48 memset(h, 0, sizeof(h)); 49 for (int i=1; i<=n; ++i) { 50 int c = s[i] - 'a'; 51 if (trans[p][c]) p = trans[p][c], now ++; 52 else { 53 for (; p&&!trans[p][c]; p=fa[p]); 54 if (!p) now = 0, p = 1; 55 else now = len[p] + 1, p = trans[p][c]; 56 } 57 h[p] = max(h[p], now); 58 } 59 for (int i=Index; i>=1; --i) { // 拓扑倒序,parent树中从深度深的到浅的 60 int t = sa[i]; 61 mn[t] = min(mn[t], h[t]); 62 if (h[t] && fa[t]) h[fa[t]] = len[fa[t]]; 63 } 64 } 65 void solve() { 66 build(); 67 memset(mn, 0x3f, sizeof(mn)); 68 while (scanf("%s",s+1) != EOF) calcc(); 69 int ans = 0; 70 for (int i=1; i<=Index; ++i) ans = max(ans, mn[i]); 71 printf("%d",ans); 72 } 73 }sam; 74 75 int main() { 76 sam.solve(); 77 return 0; 78 }
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 inline int read() { 6 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 7 for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 8 } 9 10 const int N = 200010; 11 12 struct SuffixAutomaton{ 13 int Last, Index, res, cur, fa[N], trans[N][26], len[N]; 14 SuffixAutomaton() {Last = Index = cur = 1; res = 0;} 15 void extend(int c) { 16 int P = Last, NP = ++Index; 17 len[NP] = len[P] + 1; 18 for (; P&&!trans[P][c]; P=fa[P]) trans[P][c] = NP; 19 if (!P) fa[NP] = 1; 20 else { 21 int Q = trans[P][c]; 22 if (len[P] + 1 == len[Q]) fa[NP] = Q; 23 else { 24 int NQ = ++Index; 25 fa[NQ] = fa[Q]; 26 len[NQ] = len[P] + 1; 27 memcpy(trans[NQ], trans[Q], sizeof trans[Q]); 28 fa[Q] = NQ; 29 fa[NP] = NQ; 30 for (; P&&trans[P][c]==Q; P=fa[P]) trans[P][c] = NQ; 31 } 32 } 33 Last = NP; 34 } 35 int solve(int c) { 36 if (trans[cur][c]) {cur = trans[cur][c]; res++; return res;} 37 for (; cur&&!trans[cur][c]; cur=fa[cur]); 38 if (!cur) res = 0, cur = 1; 39 else res = len[cur] + 1, cur = trans[cur][c]; 40 return res; 41 } 42 }sam[9]; 43 44 char s[N]; 45 char str[N]; 46 47 int main() { 48 int n = 0,t = 0,len; 49 scanf("%s",str+1); 50 51 while (scanf("%s",s+1)!=EOF) { 52 len = strlen(s + 1); 53 for (int i=1; i<=len; ++i) 54 sam[t].extend(s[i] - 'a'); 55 t ++; 56 } 57 int ans = 0; 58 len = strlen(str+1); 59 for (int i=1; i<=len; ++i) { 60 int tmp = 1e9; 61 for (int j=0; j<t; ++j) 62 tmp = min(tmp, sam[j].solve(str[i] - 'a')); 63 ans = max(ans, tmp); 64 } 65 printf("%d",ans); 66 return 0; 67 }