bzoj 1396 识别子串 & bzoj2865 字符串识别 后缀数组+线段树

题面

题目传送门
双倍经验传送门

解法

解法全靠yy……

  • 显然我们可以先构造出后缀数组。我们令 l e n [ i ] = m a x ( h e i g h t [ r n k [ i ] ] , h e i g h t [ r n k [ i ] + 1 ] ) len[i]=max(height[rnk[i]],height[rnk[i]+1]) ,表示从 i i 开始长度超过 l e n [ i ] len[i] 的所有子串在整个串中只出现过一次。
  • 那么我们考虑如何对于每一个位置 i i 求解答案。显然,如果确定了这个字符串的开头 j j ,那么整个字符串长度的最小值也自然可以确定,为 m a x ( j + l e n [ j ] , i ) j + 1 max(j+len[j],i)-j+1 。那么 a n s [ i ] = m i n { m a x ( j + l e n [ j ] , i ) j + 1 } ans[i]=min\{max(j+len[j],i)-j+1\} 。如果直接暴力做复杂度是 O ( n 2 ) O(n^2) 的,无法接受。
  • 我们可以考虑从小到大枚举 i i ,那么可以选择的 j j 的集合也在不断增加。因为式子中有 m a x ( j + l e n [ j ] , i ) max(j+len[j],i) 这一项,所以我们可以考虑如何消掉它。显然,如果存在某一个 j j 满足 j + l e n [ j ] i j+len[j]≤i ,那么这一项在后面的时候一定是等于 i i 的。那么对于满足这个条件的 j j 我们只要选择最大的就可以了。如果存在某一个 j + l e n [ j ] > n j+len[j]>n ,那么可以之间删掉,因为这个一定是不可能作为起点的。
  • 然后考虑那些剩下的部分,将式子展开就可以得到 l e n [ j ] + 1 len[j]+1 ,那么我们只要维护 l e n [ j ] + 1 len[j]+1 的最小值就可以了。
  • 现在问题就变成了如何得到 i < j + l e n [ j ] n i<j+len[j]≤n 的那些 j j 以及它们 l e n [ j ] + 1 len[j]+1 的最小值。这个可以直接用一个小根堆+一个大根堆(支持删除)+一个线段树来维护剩下的答案就可以了。
  • 时间复杂度: O ( n log n ) O(n\log n)

代码

#include <bits/stdc++.h>
#define inf 1 << 30
#define N 500010
using namespace std;
struct Node {
	int x, id;
	bool operator > (const Node &a) const {return x > a.x;}
	bool operator < (const Node &a) const {return x < a.x;}
	bool operator == (const Node &a) const {return x == a.x && id == a.id;}
};
struct SegmentTree {
	int mn[N * 4];
	#define lc k << 1
	#define rc k << 1 | 1
	void build(int k, int l, int r) {
		if (l == r) return mn[k] = inf, void();
		int mid = (l + r) >> 1;
		build(lc, l, mid), build(rc, mid + 1, r);
	}
	void modify(int k, int l, int r, int x, int v) {
		if (l == r) return mn[k] = v, void();
		int mid = (l + r) >> 1;
		if (x <= mid) modify(lc, l, mid, x, v);
			else modify(rc, mid + 1, r, x, v);
		mn[k] = min(mn[lc], mn[rc]);
	}
	int query(int k, int l, int r, int L, int R) {
		if (L <= l && r <= R) return mn[k];
		int mid = (l + r) >> 1, ret = inf;
		if (L <= mid) ret = min(ret, query(lc, l, mid, L, R));
		if (R > mid) ret = min(ret, query(rc, mid + 1, r, L, R));
		return ret;
	}
} T;
char st[N];
int n, m, s[N], y[N], ht[N], sa[N], len[N], rnk[N];
void Sort() {
	for (int i = 1; i <= m; i++) s[i] = 0;
	for (int i = 1; i <= n; i++) s[rnk[i]]++;
	for (int i = 1; i <= m; i++) s[i] += s[i - 1];
	for (int i = n; i; i--) sa[s[rnk[y[i]]]--] = y[i];
}
void build() {
	n = strlen(st + 1), m = 200;
	for (int i = 1; i <= n; i++) rnk[i] = st[i], y[i] = i;
	Sort(); int len = 0;
	for (int k = 1; k <= n; k <<= 1, m = len, len = 0) {
		for (int i = n - k + 1; i <= n; i++) y[++len] = i;
		for (int i = 1; i <= n; i++) if (sa[i] > k) y[++len] = sa[i] - k;
		Sort(), swap(rnk, y), rnk[sa[1]] = len = 1;
		for (int i = 2; i <= n; i++)
			rnk[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? len : ++len;
		if (len >= n) break;
	}
	for (int i = 1, k = 0; i <= n; i++) {
		if (k) k--; int j = sa[rnk[i] - 1];
		while (st[i + k] == st[j + k]) k++;
		ht[rnk[i]] = k;
	}
}
int main() {
	cin >> st + 1; build();
	for (int i = 1; i <= n; i++) {
		int pos = rnk[i];
		len[i] = max(ht[pos], ht[pos + 1]);
	}
	T.build(1, 1, n); int mx = -inf;
	priority_queue <Node, vector <Node>, greater <Node> > h1, d1;
	priority_queue <Node, vector <Node>, less <Node> > h2, d2;
	for (int i = 1; i <= n; i++) {
		h1.push((Node) {i + len[i], i});
		h2.push((Node) {i + len[i], i});
		T.modify(1, 1, n, i, len[i] + 1);
		while (true) {
			while (!d1.empty() && h1.top() == d1.top()) h1.pop(), d1.pop();
			if (h1.empty()) break; Node tmp = h1.top();
			if (tmp.x >= i) break; h1.pop(), d2.push(tmp);
			int x = tmp.id; mx = max(mx, x), T.modify(1, 1, n, x, inf);
		}
		while (true) {
			while (!d2.empty() && h2.top() == d2.top()) h2.pop(), d2.pop();
			if (h2.empty()) break; Node tmp = h2.top();
			if (tmp.x <= n) break; h2.pop(), d1.push(tmp);
			int x = tmp.id; T.modify(1, 1, n, x, inf);
		}
		int ans = min(T.query(1, 1, n, 1, i), i - mx + 1);
		cout << ans << '\n';
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/84330417