P5028 Annihilate

(color{#0066ff}{ 题目描述 })

黑暗之主的蜈蚣几乎可以毁灭一切,因此小正方形陷入了苦战……

小正方形现在需要减弱黑暗之主的攻击。

一个黑暗之主的攻击可以用一个仅有小写字母的字符串表示。

现在黑暗之主向小正方形发动了若干攻击,对于两个攻击,小正方形能选出它们最长的公共子串,并把这一段消除。

现在小正方形想要知道,对于任意两个黑暗之主的攻击,它们的最长公共子串长度是多少,你能帮帮它吗?

(color{#0066ff}{输入格式})

第一行为一个整数 (n),表示字符串数目。

接下来 (n) 行,一行一个字符串,保证所有字符串长度之和 <= 1000000

(color{#0066ff}{输出格式})

输出共有(n) 行,每行 (n - 1)个正整数

第一行第一个正整数表示第1个串与第2个串的最长公共子串

第二个正整数表示第1个串与第3个串的最长公共子串

……

第二行第一个正整数表示第2个串与第1个串的最长公共子串

以此类推。

(color{#0066ff}{输入样例})

3
abb
bcc
aba

(color{#0066ff}{输出样例})

1 2
1 1
2 1

(color{#0066ff}{数据范围与提示})

对于 (30\%) 的数据,(n leq 5),每个字符串长度 (leq 500)
对于 (100\%) 的数据,(2leq nleq 50),字符串长度之和 (leq 1000000)
注意:本题内存限制仅为 64 MB,请尽量使用内存运用优秀的方法。

另外,对于占 60 Pts 的测试点,您每通过一个点即可获得 10 Pts

对于剩下的测试点,您只有全部通过才能获得 40 Pts.

对于所有数据点,不保证数据为随机生成。

(color{#0066ff}{ 题解 })

呜呜呜~~~~这题卡SAM!!!(64MB)

于是写SA

把所有字符串拼起来,每两个之间用完全不同的xjb字符连接

然后跑SA

要找最长公共字串,显然(O(n^2len))是过不了的qwq

考虑(O(nlen))的方法

我们要找的是最长的,又因为ans是一段h的min,所以肯定找最近的(贪心)

记pre[i]为当前第i个字符串最近一次出现的位置

然后每次用ST表查询区间min

TM倪大野, 还尼玛卡我空间

ST表TM会MLE7个点

于是,pre[i]不再维护位置了,维护最近一次出现的位置到当前的(h_{min})

这样每次更新一下,就行了

恶心

#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL in() {
	char ch; int x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
const int maxn = 1e6 + 52;
const int inf = 0x7f7f7f7f;
int rk[maxn], x[maxn], y[maxn], c[maxn], sa[maxn], len[55], s[maxn], ht[maxn];
int n, m, t;
void SA() {
	m = 600, t = len[n] + 1;
	for(int i = 1; i <= t; i++) c[x[i] = s[i]]++;
	for(int i = 1; i <= m; i++) c[i] += c[i - 1];
	for(int i = t; i >= 1; i--) sa[c[x[i]]--] = i;
	for(int k = 1; k <= t; k <<= 1) {
		int num = 0;
		for(int i = t - k + 1; i <= t; i++) y[++num] = i;
		for(int i = 1; i <= t; i++) if(sa[i] > k) y[++num] = sa[i] - k;
		for(int i = 1; i <= m; i++) c[i] = 0;
		for(int i = 1; i <= t; i++) c[x[i]]++;
		for(int i = 1; i <= m; i++) c[i] += c[i - 1];
		for(int i = t; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
		std::swap(x, y);
		x[sa[1]] = 1, num = 1;
		for(int i = 2; i <= t; i++) 
			x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k])? num : ++num;
		if(num == t) break;
		m = num;
	}
	for(int i = 1; i <= t; i++) rk[i] = x[i];
	int h = 0;
	for(int i = 1; i <= t; i++) {
		if(rk[i] == 1) continue;
		if(h) h--;
		int j = sa[rk[i] - 1];
		while(j + h <= t && s[i + h] == s[j + h]) h++;
		ht[rk[i]] = h;
	}
}
int ans[55][55];
int bel[maxn];
int pre[55];
int main() {
	n = in();
	len[0] = -1;
	for(int i = 1; i <= n; i++) {
		char ch;
		while(!isalpha(ch = getchar()));
		for(len[i] = len [i - 1] + 1, s[++len[i]] = ch, bel[len[i]] = i; isalpha(ch = getchar()); s[++len[i]] = ch, bel[len[i]] = i);
		s[len[i] + 1] = 300 + i;
	}
	SA();
	for(int i = 2; i <= t; i++) {
		for(int j = 1; j <= n; j++) pre[j] = std::min(pre[j], ht[i]);
		int b = bel[sa[i]];
		pre[bel[sa[i - 1]]] = ht[i];
		if(!b) continue;
		for(int j = 1; j <= n; j++) ans[j][b] = ans[b][j] = std::max(ans[b][j], pre[j]);
	}
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			if(i == j) continue;
			printf("%d ", ans[i][j]);
		}
		puts("");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/olinr/p/10254835.html