bzoj 3238 [Ahoi2013]差异 后缀数组+单调栈

题面

题目传送门

解法

显然,我们只要求出两两后缀的lcp的和即可

两个后缀的lcp长度为这两个后缀的rnk区间height的最小值

那么可以发现,对于\(height_i\),我们可以求出以它为最小值的区间个数,然后乘起来即可

求区间可以使用单调栈解决

时间复杂度:\(O(n\ log\ n)\)

代码

#include <bits/stdc++.h>
#define N 500010
using namespace std;
char st[N];
struct SuffixArray {
	int x[N], y[N], sa[N], rnk[N], cnt[N], height[N];
	int l[N], r[N];
	void build() {
		int n = strlen(st + 1), m = 122;
		for (int i = 1; i <= n; i++) cnt[x[i] = st[i]]++;
		for (int i = 2; i <= m; i++) cnt[i] += cnt[i - 1];
		for (int i = n; i; i--) sa[cnt[x[i]]--] = i;
		for (int k = 1; k <= n; k <<= 1) {
			int num = 0;
			for (int i = n - k + 1; i <= n; i++) y[++num] = i;
			for (int i = 1; i <= n; i++)
				if (sa[i] > k) y[++num] = sa[i] - k;
			for (int i = 1; i <= m; i++) cnt[i] = 0;
			for (int i = 1; i <= n; i++) cnt[x[i]]++;
			for (int i = 2; i <= m; i++) cnt[i] += cnt[i - 1];
			for (int i = n; i; i--) sa[cnt[x[y[i]]]--] = y[i], y[i] = 0;
			swap(x, y); x[sa[1]] = 1, num = 1;
			for (int i = 2; i <= n; i++)
				x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) ? num : ++num;
			if (num == n) break; m = num;
		}
		for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
		int k = 0;
		for (int i = 1; i <= n; i++) {
			if (rnk[i] == 1) continue;
			if (k) k--; int j = sa[rnk[i] - 1];
			while (i + k <= n && j + k <= n && st[i + k] == st[j + k]) k++;
			height[rnk[i]] = k;
		}
	}
	void solve() {
		int n = strlen(st + 1); stack <int> s;
		for (int i = n; i; i--) {
			while (!s.empty() && height[s.top()] > height[i])
				l[s.top()] = i + 1, s.pop();
			s.push(i);
		}
		while (!s.empty()) l[s.top()] = 1, s.pop();
		for (int i = 1; i <= n; i++) {
			while (!s.empty() && height[s.top()] >= height[i])
				r[s.top()] = i - 1, s.pop();
			s.push(i);
		}
		while (!s.empty()) r[s.top()] = n, s.pop();
		long long ans = 1ll * n * (n + 1) * (n - 1) / 2;
		for (int i = 1; i <= n; i++)
			ans -= 2ll * (i - l[i] + 1) * (r[i] - i + 1) * height[i];
		cout << ans << "\n";
	}
} SA;
int main() {
	scanf(" %s", st + 1);
	SA.build(); SA.solve();
	return 0;
}

posted @ 2018-08-14 22:21  谜のNOIP  阅读(163)  评论(0编辑  收藏  举报