【BZOJ4755】【JSOI2016】扭动的回文串

【题目链接】

【思路要点】

  • 一定存在一个最长的扭动回文串是从回文中心所在串的某一个极长回文子串开始,一侧向回文中心所在串匹配,另一侧扭向另一个串匹配。因为任意一个扭动的回文串都对应了一个同样长的满足上述要求的扭动的回文串。
  • 枚举回文中心,问题转化为了两个字符串的LCP,字符串哈希和后缀结构均可解决。
  • 时间复杂度\(O(NLogN)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
#define MAXN	400005
#define MAXLOG	20
int n, lenx[MAXN], leny[MAXN];
char x[MAXN], y[MAXN];
int homex[MAXN], homey[MAXN];
int Manacher(char *x, int *len) {
	static char s[MAXN];
	for (int i = 1; i <= n; i++) {
		s[i * 2] = x[i];
		s[i * 2 - 1] = '#';
	}
	s[0] = '$'; s[n * 2 + 1] = '#';
	s[n * 2 + 2] = '$';
	int pos = 0, home = 0, ans = 0;
	for (int i = 1; i <= 2 * n + 1; i++) {
		if (i > pos) {
			len[i] = 1;
			while (s[i - len[i]] == s[i + len[i]])
				len[i]++;
			pos = i + len[i] - 1;
			home = i;
		} else {
			int p = home * 2 - i;
			if (i + len[p] - 1 < pos) len[i] = len[p];
			else {
				len[i] = pos - i + 1;
				while (s[i - len[i]] == s[i + len[i]])
					len[i]++;
				pos = i + len[i] - 1;
				home = i;
			}
		}
		ans = max(ans, len[i] - 1);
	}
	return ans;
}
struct Suffix_Automaton {
	int child[MAXN][26];
	int father[MAXN], depth[MAXN];
	int parent[MAXN][MAXLOG], cnt[MAXN];
	vector <int> a[MAXN];
	int root, size, last;
	int new_node(int dep) {
		depth[size] = dep;
		father[size] = 0;
		a[size].clear();
		memset(child[size], 0, sizeof(child[size]));
		memset(parent[size], 0, sizeof(parent[size]));
		return size++;
	}
	void init() {
		size = 0;
		root = new_node(0);
	}
	void Extend(int ch) {
		int np = child[last][ch];
		if (np) {
			if (depth[last] + 1 == depth[np]) last = np;
			else {
				int nq = new_node(depth[last] + 1);
				father[nq] = father[np];
				father[np] = nq;
				memcpy(child[nq], child[np], sizeof(child[np]));
				for (int p = last; child[p][ch] == np; p = father[p])
					child[p][ch] = nq;
				last = nq;
			}
		} else {
			np = new_node(depth[last] + 1);
			int p = last;
			for (; child[p][ch] == 0; p = father[p])
				child[p][ch] = np;
			if (child[p][ch] == np) last = np;
			else {
				int q = child[p][ch];
				if (depth[q] == depth[p] + 1) {
					father[np] = q;
					last = np;
				} else {
					int nq = new_node(depth[p] + 1);
					father[nq] = father[q];
					father[q] = father[np] = nq;
					memcpy(child[nq], child[q], sizeof(child[q]));
					for (; child[p][ch] == q; p = father[p])
						child[p][ch] = nq;
					last = np;
				}
			}
		}
	}
	void insert(char *s, int *home) {
		int len = strlen(s + 1);
		last = root;
		for (int i = 1; i <= len; i++) {
			Extend(s[i] - 'A');
			home[i] = last;
		}
	}
	void work(int pos, int depth) {
		cnt[pos] = depth;
		parent[pos][0] = father[pos];
		for (int i = 1; i < MAXLOG; i++)
			parent[pos][i] = parent[parent[pos][i - 1]][i - 1];
		for (unsigned i = 0; i < a[pos].size(); i++)
			work(a[pos][i], depth + 1);
	}
	void build() {
		for (int i = 1; i < size; i++)
			a[father[i]].push_back(i);
		work(root, 0);
	}
	int query(int x, int y) {
		if (cnt[x] < cnt[y]) swap(x, y);
		for (int i = MAXLOG - 1; i >= 0; i--)
			if (cnt[parent[x][i]] >= cnt[y]) x = parent[x][i];
		if (x == y) return depth[x];
		for (int i = MAXLOG - 1; i >= 0; i--)
			if (parent[x][i] != parent[y][i]) {
				x = parent[x][i];
				y = parent[y][i];
			}
		return depth[father[x]];
	}
} SAM;
int main() {
	freopen("BZOJ4755.in", "r", stdin);
	freopen("BZOJ4755.out", "w", stdout);
	scanf("%d\n%s\n%s", &n, x + 1, y + 1);
	int ans = max(Manacher(x, lenx), Manacher(y, leny));
	SAM.init();
	SAM.insert(x, homex);
	for (int i = 1, j = n; i < j; i++, j--)
		swap(y[i], y[j]);
	SAM.insert(y, homey);
	for (int i = 1, j = n; i < j; i++, j--)
		swap(homey[i], homey[j]);
	SAM.build();
	for (int i = 1; i <= n; i++) {
		int tmp = lenx[i * 2] / 2;
		if (tmp == i) continue;
		ans = max(ans, 2 * SAM.query(homex[i - tmp], homey[i + tmp - 1]) + tmp * 2 - 1);
	}
	for (int i = 1; i < n; i++) {
		int tmp = lenx[i * 2 + 1] / 2;
		if (tmp == i) continue;
		ans = max(ans, 2 * SAM.query(homex[i - tmp], homey[i + tmp]) + tmp * 2);
	}
	for (int i = 1; i <= n; i++) {
		int tmp = leny[i * 2] / 2;
		if (i + tmp > n) continue;
		ans = max(ans, 2 * SAM.query(homex[i - tmp + 1], homey[i + tmp]) + tmp * 2 - 1);
	}
	for (int i = 1; i < n; i++) {
		int tmp = leny[i * 2 + 1] / 2;
		if (i + tmp + 1 > n) continue;
		ans = max(ans, 2 * SAM.query(homex[i - tmp + 1], homey[i + tmp + 1]) + tmp * 2);
	}
	printf("%d\n", ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值