【BZOJ2946】公共串 [SAM]
2017-05-15 17:14
134 查看
公共串
Time Limit: 3 Sec Memory Limit: 128 MB[Submit][Status][Discuss]
Description
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果
Input
文件的第一行是整数 n ,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。
Output
仅一行,一个整数,最长公共子串的长度。
Sample Input
3abcb
bca acbc
Sample Output
2HINT
2 <= n <= 5
Solution
因为要求所有串的最长公共子串,所以我们运用SAM,先对第一个串(基本串)构建一个SAM,然后用后面的串匹配即可。
记录 L[i] 表示当前串和基本串在 i 这个状态匹配的最长长度。显然,一个状态对答案的贡献是所有串和基本串匹配时 L[i] 的最小值。
然后取一个最大值即可。
Code
#include<iostream> #include<string> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> using namespace std; const int ONE=4005; const int INF=2147483640; int T,n; char str[ONE]; int ans[ONE], q[ONE], L[ONE]; int len[ONE], a[ONE][27], fa[ONE], v[ONE]; int last, cnt; int Ans; int get() { int res=1,Q=1;char c; while( (c=getchar())<48 || c>57 ) if(c=='-')Q=-1; res=c-48; while( (c=getchar())>=48 && c<=57 ) res=res*10+c-48; return res*Q; } struct SAM { SAM() {last = cnt = 1;} void Add(int c) { int x = last, New = last = ++cnt; len[New] = len[x] + 1; while(x && !a[x][c]) a[x][c] = New, x = fa[x]; if(!x) {fa[New] = 1; return;} int q = a[x][c]; if(len[x] + 1 == len[q]) fa[New] = q; else { int Nq = ++cnt; len[Nq] = len[x] + 1; memcpy(a[Nq], a[q], sizeof(a[q])); fa[Nq] = fa[q]; fa[New] = fa[q] = Nq; while(a[x][c] == q) a[x][c] = Nq, x = fa[x]; } } void Pre() { for(int i=1; i<=cnt; i++) v[len[i]]++; for(int i=1; i<=cnt; i++) ans[i] = len[i]; for(int i=1; i<=n; i++) v[i] += v[i-1]; for(int i=cnt; i>=1; i--) q[v[len[i]]--] = i; } }; SAM S; void Check() { memset(L, 0, sizeof(L)); n = strlen(str+1); int x = 1, record = 0; for(int i=1; i<=n; i++) { int c = str[i]-'a'+1; while(x && !a[x][c]) x = fa[x]; if(!x) {x = 1; record = 0; continue;} record = min(record, len[x]) + 1; x = a[x][c]; L[x] = max(L[x], record); } for(int i=cnt; i>=1; i--) L[fa[q[i]]] = max(L[fa[q[i]]], L[q[i]]); for(int i=1; i<=cnt; i++) ans[i] = min(ans[i], L[i]); } int main() { T = get(); T --; scanf("%s", str+1); n = strlen(str+1); for(int i=1; i<=n; i++) S.Add(str[i]-'a'+1); S.Pre(); while(T--) { scanf("%s", str+1); Check(); } for(int i=1; i<=cnt; i++) Ans = max(Ans, ans[i]); printf("%d",Ans); }View Code
相关文章推荐
- 【BZOJ 2946】 2946: [Poi2000]公共串 (SAM)
- bzoj2946 [Poi2000]公共串(SA,SAM)
- BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案
- [BZOJ 2946]公共串
- bzoj2946 [Poi2000]公共串
- BZOJ 2946 SA/SAM
- 【BZOJ 2946】[Poi2000]公共串 后缀数组
- 【bzoj2946】[Poi2000]公共串
- [bzoj2946]公共串
- bzoj 2946: [Poi2000]公共串
- bzoj2946 [Poi2000]公共串
- BZOJ2946: [Poi2000]公共串
- 【BZOJ】【2946】【POI2000】公共串
- [BZOJ2946] [Poi2000]公共串解题报告|后缀数组
- bzoj 2946: [Poi2000]公共串 后缀自动机
- bzoj2946 [Poi2000]公共串(后缀数组 || 后缀自动机)
- 【bzoj2946】[Poi2000]公共串 后缀自动机
- BZOJ2946: [Poi2000]公共串
- BZOJ2946 [Poi2000]公共串 二分+hash
- BZOJ 2946 Poi2000 公共串 后缀自动机