Codeforces 700E Cool Slogans

Codeforces 700E Cool Slogans

题目大意:给出一个字符串 (S),让你构造一个序列 (S_1,S_2dots S_k),满足 (forall S_i) (S_i)(S) 的字串,且对于 (i > 1)(S_i)(S_{i-1}) 中出现了至少两次,最大化 (k) 的长度。(|S|leq 2 imes 10^5)

解题思路

可以证明一个很牛逼的结论,对于 ( ext{parents}) 树上的一个节点 (u) ,其在其子树中的任意节点 (v) 代表的串 (maxv) ,节点 (u) 代表的所有串在 (maxv) 中的所有串出现次数相同。

假设有两个串出现次数不同,那么一定存在一个出现位置使得 (u​) 代表的一个串 (k​)(maxv​) 的前缀但是 (maxu) 不是,这样会发现 (maxu​)(k​)( ext{right}​) 集合不同,矛盾。

那么有了这个结论每个节点随便选个长度代表它都是一样的,显然每个串选 (maxu) 对于其祖先来说最优,然后就可以在 (parent) 拿节点做状态了。

贪心地从后往前做,相当于在 (parent) 树从根往下做能放就放,正确性显然。令 (f[u]) 表示当前考虑到 (u) 节点的最长答案,那么只需要判断 (u) 节点的 (max) 能否放在上一个选的串前面即可。由于 (right) 集合是一个等价类,那么对于每个串的所有出现位置只需要取最前面一个即可,然后用线段树合并维护 (right) 集合判断上一个串是否出现两次即可。由于上一个串是 (u) 的一个祖先,还需要记一下上一个选的串对应节点是什么,复杂度 (O(|S|logn))

code

/*program by mangoyang*/
#include <bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
	int ch = 0, f = 0; x = 0;
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
	for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
	if(f) x = -x;
}
const int N = 400005;
char s[N]; int n, ans;
namespace Seg{
	#define mid ((l + r) >> 1)
	int lc[N*25], rc[N*25], sz[N*25], size;
	inline void ins(int &u, int l, int r, int pos){
		if(!u) u = ++size;
		if(l == r) return (void) (sz[u]++);
		if(pos <= mid) ins(lc[u], l, mid, pos);
		else ins(rc[u], mid + 1, r, pos); 
		sz[u] = sz[lc[u]] + sz[rc[u]];
	}
	inline int merge(int x, int y, int l, int r){
		if(!x || !y) return x + y;
		int o = ++size;
		if(l == r) sz[o] = sz[x] + sz[y];
		else{
			lc[o] = merge(lc[x], lc[y], l, mid);
			rc[o] = merge(rc[x], rc[y], mid + 1, r);
			sz[o] = sz[lc[o]] + sz[rc[o]];
		}
		return o;
	}
	inline int query(int u, int l, int r, int L, int R){
		if(l >= L && r <= R) return sz[u];
		int res = 0;
		if(L <= mid) res += query(lc[u], l, mid, L, R);
		if(mid < R) res += query(rc[u], mid + 1, r, L, R);
		return res;
	}
	#undef mid
}
namespace SAM{
	vector<int> g[N];
	int ch[N][26], id[N], len[N], fa[N], dp[N], rt[N], pos[N], tail = 1, size = 1;
	inline int newnode(int x){ return len[++size] = x, size; }
	inline void ins(int c, int x){
		int p = tail, np = newnode(len[p] + 1); pos[np] = x;
		Seg::ins(rt[np], 1, n, pos[np]);
		for(; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
		if(!p) return (void) (fa[np] = 1, tail = np);
		int q = ch[p][c];
		if(len[q] == len[p] + 1) fa[np] = q;
		else{
			int nq = newnode(len[p] + 1); pos[nq] = x;
			fa[nq] = fa[q], fa[q] = fa[np] = nq;
			for(int i = 0; i < 26; i++) ch[nq][i] = ch[q][i];
			for(; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
		}tail = np;
	}
	inline void addedge(){
		for(int i = 2; i <= size; i++) g[fa[i]].push_back(i);
	}
	inline void dfs(int u){
		for(int i = 0; i < (int) g[u].size(); i++)
			dfs(g[u][i]), rt[u] = Seg::merge(rt[u], rt[g[u][i]], 1, n);
	}
	inline void dfs2(int u){
		if(u > 1){
			if(fa[u] == 1) id[u] = u, dp[u] = 1;
			else{
				int tmp = Seg::query(rt[id[fa[u]]], 1, n, pos[u] - len[u] + len[id[fa[u]]], pos[u] - 1);
				if(tmp) dp[u] = dp[fa[u]] + 1, id[u] = u; else dp[u] = dp[fa[u]], id[u] = id[fa[u]]; 			
			}
		}
		ans = max(ans, dp[u]);
		for(int i = 0; i < (int) g[u].size(); i++) dfs2(g[u][i]);
	}
}
int main(){
	read(n), scanf("%s", s + 1);
	for(int i = 1; i <= n; i++)
		SAM::ins(s[i] - 'a', i);
	SAM::addedge(), SAM::dfs(1), SAM::dfs2(1);
	cout << ans << endl;
	return 0;
}
原文地址:https://www.cnblogs.com/mangoyang/p/10409198.html