有一个结论:如果答案不是Infinity,那么最长回文子串只可能出现在一个串里,或者两个串拼接一次形成的串里。
那么我们枚举每个串的每个回文中心,对于剩下的不在回文串里的子串,去其他串里找一个拼接串,看能不能形成更大的回文串。
于是我们需要一个快速查询LCP的数据结构,选择后缀数组+ST表就可以了。
但是这样做复杂度还是比较高,于是我们考虑用图论模型优化。
先把单个串是回文串的情况特判掉。
对每个位置建两个点,分别代表当前位置前面/后面的剩余部分。如果可以剩余部分可以和其他串形成一个更大的回文串, 那么从这个剩余部分代表的点向目标点连边,边权为回文串长度。
对于这种情况:AB,A和B分别是个回文串,那么这种情况是Infinity,我们需要建立一个特殊节点,走到这个特殊节点的也算是Infinity。
当然,如果形成了一个环,也是Infinity。
否则答案就是最长路。
不太好说,看代码吧orz。
/* Telekinetic Forest Guard */
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 220005, maxm = 22000005;
int n, head[maxn], cnt, bin[20], lg[maxn], len[maxn], slen[maxn];
struct _edge {
int v, w, next;
} g[maxm];
inline void addedge(int u, int v, int w) {
g[cnt] = (_edge){v, w, head[u]};
head[u] = cnt++;
}
/* SA and ST */
int lc[maxn], rc[maxn], sa[maxn], rank[maxn], height[maxn], st[20][maxn];
int s[maxn], sym, m;
int wa[maxn], wb[maxn], wv[maxn], tmp[maxn];
inline void getSA(int *r, int n, int m) {
int *x = wa, *y = wb;
for(int i = 0; i < m; i++) tmp[i] = 0;
for(int i = 0; i < n; i++) tmp[x[i] = r[i]]++;
for(int i = 1; i < m; i++) tmp[i] += tmp[i - 1];
for(int i = n - 1; i >= 0; i--) sa[--tmp[x[i]]] = i;
for(int j = 1; j < n; j <<= 1) {
int p = 0;
for(int i = n - j; i < n; i++) y[p++] = i;
for(int i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
for(int i = 0; i < m; i++) tmp[i] = 0;
for(int i = 0; i < n; i++) tmp[wv[i] = x[y[i]]]++;
for(int i = 1; i < m; i++) tmp[i] += tmp[i - 1];
for(int i = n - 1; i >= 0; i--) sa[--tmp[wv[i]]] = y[i];
swap(x, y);
p = 1; x[sa[0]] = 0;
for(int i = 1; i < n; i++)
x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j] ? p - 1 : p++;
if(p >= n) break;
m = p;
}
}
inline void getHeight(int *r, int n) {
int i, j, k;
for(i = j = k = 0; i < n; height[rank[i++]] = k)
for(k ? k-- : 0, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; k++);
}
inline void getST(int n) {
for(int i = 1; i <= n; i++) st[0][i] = height[i];
for(int i = 1; i <= lg[n]; i++)
for(int j = 1; j + bin[i] - 1 <= n; j++)
st[i][j] = min(st[i - 1][j], st[i - 1][j + bin[i - 1]]);
}
inline int stquery(int x, int y) {
x = rank[x]; y = rank[y];
if(x > y) swap(x, y); x++;
int k = lg[y - x + 1];
return min(st[k][x], st[k][y - bin[k] + 1]);
}
inline int query(int a, int x, int b, int y) {
if(x > 0) a = lc[a] + x - 1;
else a = rc[a] + (len[a] + x);
if(y > 0) b = lc[b] + y - 1;
else b = rc[b] + (len[b] + y);
return stquery(a, b);
}
int S, T, T_;
LL dis[maxn];
int du[maxn], q[maxn];
bool vis[maxn], inq[maxn];
inline bool loop(int x) {
vis[x] = inq[x] = 1;
for(int i = head[x]; ~i; i = g[i].next) {
du[g[i].v]++;
if(inq[g[i].v] || (!vis[g[i].v] && loop(g[i].v))) return 1;
}
return inq[x] = 0;
}
inline int getid(int x, int y) {
if(x == 0) return S;
if(x == -1) return T;
if(y == 0 || y > len[x]) return T_;
if(y > 0) return slen[x - 1] * 2 + y;
return slen[x - 1] * 2 + len[x] - y;
}
inline void add(int a, int x, int b, int y, int w) {
a = getid(a, x); b = getid(b, y);
addedge(a, b, w);
}
char str[maxn];
int main() {
bin[0] = 1;
for(int i = 1; i < 20; i++) bin[i] = bin[i - 1] << 1;
lg[0] = -1;
for(int i = 1; i < maxn; i++) lg[i] = lg[i >> 1] + 1;
sym = 26;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%s", str);
len[i] = strlen(str);
slen[i] = slen[i - 1] + len[i];
s[m++] = ++sym; lc[i] = m;
for(int j = 0; j < len[i]; j++) s[m++] = str[j] - 'a' + 1;
s[m++] = ++sym; rc[i] = m;
for(int j = 0; j < len[i]; j++) s[m++] = str[len[i] - j - 1] - 'a' + 1;
}
getSA(s, m + 1, 300);
for(int i = 0; i <= m; i++) rank[sa[i]] = i;
getHeight(s, m);
getST(m);
S = 0; T = slen[n] * 2 + 10; T_ = T + 1;
bool flag = 1;
for(int i = 1; i <= n; i++) if(query(i, 0, i, -(len[i] + 1)) == len[i])
flag = 0;
for(int i = 0; i < maxn; i++) head[i] = -1;
LL ans = 0;
for(int i = 1; flag && i <= n; i++) {
for(int j = 1; j <= len[i]; j++) {
int r = query(i, j, i, -j), l = min(j, len[i] - j + 1);
ans = max(ans, (LL)r * 2 - 1);
if(r == l) {
if(j > len[i] - j + 1) add(0, 0, i, -(j - r), r * 2 - 1);
else add(0, 0, i, j + r, r * 2 - 1);
}
}
for(int j = 2; j <= len[i]; j++) {
int r = query(i, j, i, -(j - 1)), l = min(j - 1, len[i] - j + 1);
ans = max(ans, (LL)r * 2);
if(r == l) {
if(j - 1 > len[i] - j + 1) add(0, 0, i, -(j - 1 - r), r * 2);
else add(0, 0, i, j + r, r * 2);
}
}
add(0, 0, i, 1, 0);
add(0, 0, i, -len[i], 0);
for(int j = 1; j <= len[i]; j++) {
int wf = 0, wr = 0;
for(int k = 1; k <= n; k++) {
int r = query(i, j, k, -len[k]);
if(r == len[i] - j + 1) add(i, j, k, -(len[k] - r), r * 2);
else if(r == len[k]) add(i, j, i, j + r, r * 2);
else wf = max(wf, r * 2);
r = query(i, -j, k, 1);
if(r == j) add(i, -j, k, r + 1, r * 2);
else if(r == len[k]) add(i, -j, i, -(j - r), r * 2);
else wr = max(wr, r * 2);
}
if(wf > 0) add(i, j, -1, 0, wf);
if(wr > 0) add(i, -j, -1, 0, wr);
}
}
for(int i = 0; i < maxn; i++) du[i] = vis[i] = inq[i] = 0;
if(!flag || loop(S) || vis[T_]) ans = -1;
else {
int h = 0, t = 0;
q[t++] = S;
for(int i = 0; i < maxn; i++) dis[i] = 0;
while(h != t) {
int u = q[h++];
for(int i = head[u]; ~i; i = g[i].next) {
dis[g[i].v] = max(dis[g[i].v], dis[u] + g[i].w);
ans = max(ans, dis[g[i].v]);
if(!--du[g[i].v]) q[t++] = g[i].v;
}
}
}
if(!~ans) printf("Infinity\n");
else printf("%lld\n", ans);
return 0;
}