求助后缀数组
查看原帖
求助后缀数组
134000
Plozia楼主2022/2/12 14:33

样例过不去 /kk,思路和题解是一样的,就是用线段树求区间最小值而不是 st 表,求调 /kel

/*
========= Plozia =========
	Author:Plozia
	Problem:P1117 [NOI2016] 优秀的拆分
	Date:2022/2/10
========= Plozia =========
*/

#include <bits/stdc++.h>

typedef long long LL;
const int MAXN = 6e4 + 5;
int t, sa[MAXN << 1], rk[MAXN << 1], height[MAXN], n, Before[MAXN], saBefore[MAXN << 1], rkBefore[MAXN << 1], fir[MAXN], sec[MAXN];
char str[MAXN << 1], str2[MAXN << 1];
struct node
{
	int a, b, id;
	bool operator <(const node &fir)const { return (a ^ fir.a) ? (a < fir.a) : (b < fir.b); }
}a[MAXN];
struct SgT
{
	int l, r, Minn;
	#define l(k, p) tree[k][p].l
	#define r(k, p) tree[k][p].r
	#define Minn(k, p) tree[k][p].Minn
}tree[2][MAXN << 2];

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = sum * 10 + (ch ^ 48);
	return sum * fh;
}
int Max(int fir, int sec) { return (fir > sec) ? fir : sec; }
int Min(int fir, int sec) { return (fir < sec) ? fir : sec; }

void GetHeight()
{
	for (int i = 1; i <= n; ++i) rk[i] = str[i];
	for (int len = 1; len <= n; len *= 2)
	{
		for (int i = 1; i <= n; ++i) { a[i] = (node){rk[i], rk[i + len], i}; }
		std::sort(a + 1, a + n + 1); int m = 0;
		for (int i = 1; i <= n; ++i)
		{
			if (a[i].a != a[i - 1].a || a[i].b != a[i - 1].b) ++m;
			rk[a[i].id] = m;
		}
	}
	for (int i = 1; i <= n; ++i) sa[rk[i]] = i;
	int k = 1;
	for (int i = 1; i <= n; ++i)
	{
		if (k) --k; if (rk[i] == 1) continue ;
		int j = sa[rk[i] - 1];
		while (str[j + k] == str[i + k]) ++k;
		height[rk[i]] = k;
	}
	// 求 Height,其中下面这段是从上面这段复制的,就改了改数组名
	for (int i = 1; i <= n; ++i) rkBefore[i] = str2[i];
	for (int len = 1; len <= n; len *= 2)
	{
		for (int i = 1; i <= n; ++i) { a[i] = (node){rkBefore[i], rkBefore[i + len], i}; }
		std::sort(a + 1, a + n + 1); int m = 0;
		for (int i = 1; i <= n; ++i)
		{
			if (a[i].a != a[i - 1].a || a[i].b != a[i - 1].b) ++m;
			rkBefore[a[i].id] = m;
		}
	}
	for (int i = 1; i <= n; ++i) saBefore[rkBefore[i]] = i;
	k = 1;
	for (int i = 1; i <= n; ++i)
	{
		if (k) --k; if (rkBefore[i] == 1) continue ;
		int j = saBefore[rkBefore[i] - 1];
		while (str2[j + k] == str2[i + k]) ++k;
		Before[rkBefore[i]] = k;
	}
}

void Build(int k, int p, int l, int r)
{
	l(k, p) = l, r(k, p) = r;
	if (l == r) { Minn(k, p) = (k == 0) ? height[l] : Before[l]; return ; }
	int mid = (l + r) >> 1;
	Build(k, p << 1, l, mid); Build(k, p << 1 | 1, mid + 1, r);
	Minn(k, p) = Min(Minn(k, p << 1), Minn(k, p << 1 | 1));
}

int AskMin(int k, int p, int l, int r)
{
	if (l(k, p) >= l && r(k, p) <= r) return Minn(k, p);
	int mid = (l(k, p) + r(k, p)) >> 1, val = 0x7f7f7f7f;
	if (l <= mid) val = Min(val, AskMin(k, p << 1, l, r));
	if (r > mid) val = Min(val, AskMin(k, p << 1 | 1, l, r));
	return val;
}

int main()
{
	t = Read();
	while (t--)
	{
		memset(sa, 0, sizeof(sa)); memset(rk, 0, sizeof(rk));
		memset(height, 0, sizeof(height)); memset(Before, 0, sizeof(Before));
		memset(saBefore, 0, sizeof(saBefore)); memset(rkBefore, 0, sizeof(rkBefore));
		memset(fir, 0, sizeof(fir)); memset(sec, 0, sizeof(sec));
		// 全是清零
		scanf("%s", str + 1); n = strlen(str + 1);
		for (int i = 1; i <= n; ++i) str2[i] = str[n - i + 1];
		GetHeight(); Build(0, 1, 1, n); Build(1, 1, 1, n); // 建树
		for (int len = 1; len <= n; ++len)
		{
			for (int i = len; i + len <= n; i += len)
			{
				int j = i + len;
				int x = AskMin(0, 1, i, j), y = AskMin(1, 1, n - j + 2, n - i + 2); // x -> LCP, y -> LCS
				x = Min(x, len); y = Min(y, len - 1);
				if (x + y >= len)
				{
					++sec[i - y]; --sec[i + x - len + 1];
					++fir[j - y + len - 1]; --fir[j + x];
				}
			}
		} // 主部分
		for (int i = 1; i <= n; ++i) fir[i] += fir[i - 1], sec[i] += sec[i - 1];
		LL ans = 0;
		for (int i = 1; i <= n; ++i) ans += 1ll * fir[i] * sec[i + 1];
		printf("%lld\n", ans); // printf("n:%d\n", n);
		// printf("rk:"); for (int i = 1; i <= n; ++i) printf("%d%c", rk[i], " \n"[i == n]);
		// printf("sa:"); for (int i = 1; i <= n; ++i) printf("%d%c", sa[i], " \n"[i == n]);
		// printf("fir:"); for (int i = 1; i <= n; ++i) printf("%d%c", fir[i], " \n"[i == n]);
		// printf("sec:"); for (int i = 1; i <= n; ++i) printf("%d%c", sec[i], " \n"[i == n]);
		// printf("Height:"); for (int i = 1; i <= n; ++i) printf("%d%c", height[i], " \n"[i == n]);
		// printf("BeforeHeight:"); for (int i = 1; i <= n; ++i) printf("%d%c", Before[i], " \n"[i == n]);
	}
	return 0;
}
2022/2/12 14:33
加载中...