[BZOJ2946][Poi2000]公共串
[BZOJ2946][Poi2000]公共串
试题描述
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果
输入
文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。
输出
仅一行,一个整数,最长公共子串的长度。
输入示例
3 abcb bca acbc
输出示例
2
数据规模及约定
见“输入”
题解
见这道题。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 4010 #define maxa 26 char S[maxn]; int len; int ToT, rt, last, to[maxn][maxa], par[maxn], Max[maxn]; void extend(int x) { int p = last, np = ++ToT; Max[np] = Max[p] + 1; last = np; while(p && !to[p][x]) to[p][x] = np, p = par[p]; if(!p){ par[np] = rt; return ; } int q = to[p][x]; if(Max[q] == Max[p] + 1){ par[np] = q; return ; } int nq = ++ToT; Max[nq] = Max[p] + 1; memcpy(to[nq], to[q], sizeof(to[q])); par[nq] = par[q]; par[q] = par[np] = nq; while(p && to[p][x] == q) to[p][x] = nq, p = par[p]; return ; } int sa[maxn], Ws[maxn], Ans[maxn], mn[maxn]; int main() { int n = read(); rt = last = ToT = 1; scanf("%s", S); len = strlen(S); for(int i = 0; i < len; i++) extend(S[i] - 'a'); for(int i = 1; i <= ToT; i++) Ws[len-Max[i]]++; for(int i = 1; i <= len; i++) Ws[i] += Ws[i-1]; for(int i = ToT; i; i--) sa[Ws[len-Max[i]]--] = i; for(int i = 1; i <= ToT; i++) mn[i] = Max[i]; for(int k = 1; k < n; k++) { scanf("%s", S); int tmp = 0; memset(Ans, 0, sizeof(Ans)); for(int i = 0, p = rt; S[i] != '\0'; i++) { int x = S[i] - 'a'; if(to[p][x]) tmp++, p = to[p][x]; else { while(p && !to[p][x]) p = par[p]; if(!p) p = 1, tmp = 0; else tmp = Max[p] + 1, p = to[p][x]; } Ans[p] = max(Ans[p], tmp); } for(int i = 1; i <= ToT; i++) { int u = sa[i]; mn[u] = min(mn[u], Ans[u]); if(Ans[u] && par[u]) Ans[par[u]] = Max[par[u]]; } } int ans = 0; for(int i = 1; i <= ToT; i++) ans = max(ans, mn[i]); printf("%d\n", ans); return 0; }