Codeforces 700E Cool Slogans

Codeforces 700E Cool Slogans

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

解题思路

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

假设有两个串出现次数不同,那么一定存在一个出现位置使得 \(u​\) 代表的一个串 \(k​\)\(maxv​\) 的前缀但是 \(maxu\) 不是,这样会发现 \(maxu​\)\(k​\)\(\text{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;
}
posted @ 2019-02-20 21:00  Joyemang33  阅读(279)  评论(0编辑  收藏  举报