【BZOJ3654】图样图森破【最长路】【后缀数组】【ST表】【回文串】【LCP】

【题目链接】

有一个结论:如果答案不是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;
}


你可能感兴趣的:(【BZOJ3654】图样图森破【最长路】【后缀数组】【ST表】【回文串】【LCP】)