cf 700 - E Cool Slogans

Description

定义字符串\(A\)\(B\)酷 : \(B\)至少在\(A\)中出现\(2\)

给你一个长度\(\le 2*10^5\)的字符串s

求在其中选择若干子串(随便选,可重叠), 排成一列, 且任意后一个子串比前一个子串\(Cool\)的方案中

最长的长度是多少

Analysis

出现两次...选了一个串要先收缩然后找到合法前缀然后再收缩什么... 没有思路啊

我们反过来从答案入手, 对于任意一个最优解, 进行下图的收缩

即: 从下到上进行如下操作. 选择每组中最左的一个, 做一条切割线, 保留上一层切割线右的部分

这样, 每一层的字符串都是上一层的前缀

根据贪心, 我们一定选择由上一层选择一个最长的出现至少2次的前缀

Solution

考虑后缀树结构

一个子串的前缀为其在后缀树上的祖先

我们需要实现 : (1)判断原串的某个前缀是否在原串出现至少两次 (2) 求出最长的一个

判断的话, 我们可以考虑\(left\)

因为一个串出现在什么地方是没有影响的因为本质相同

因为前缀在开头出现了一次, 再出现一次就好了

所以我们可以求出原串的最左出现位置\(p\), 然后求出该前缀\(>p\)的最左出现位置

判断长度是否合法即可

因为要求出最长的合法, 而\(fa\)会导致串变短

而合法与不合法有分界线, 可以二分

所以我们树上倍增 跳出 的不合法点的极长距离, 然后再往上走一步就好了

Code

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#define rep(i,a,b) for (int i = (a); i <= (b); ++ i)
#define per(i,a,b) for (int i = (a); i >= (b); -- i)
#define For(i,a,b) for (int i = (a); i < (b); ++ i)
using namespace std;
const int M = 2e5 + 7;
const int INF = 1e9 + 7;

int n;
char s[M];

namespace Seg{
	static const int N = (M << 1) * 20 * 2;
	int n, rt[N], tot;
	struct node{
		int lc, rc;
	}a[N];

	inline int newnode(int x){
		a[++tot] = a[x];
		return tot;
	}

	void ins(int &x, int l, int r, int to){
		x = newnode(0);
		if (l == r) return;
		int mid = l+r >> 1;
		if (to <= mid) ins(a[x].lc, l, mid, to);
		else ins(a[x].rc, mid+1, r, to);
	}

	int merge(int x, int y, int l, int r){
		if (!x || !y) return x | y;
		if (l == r) return newnode(x);
		int mid = l+r >> 1;
		int nw = newnode(x);
		a[nw].lc = merge(a[x].lc, a[y].lc, l, mid);
		a[nw].rc = merge(a[x].rc, a[y].rc, mid+1, r);
		return nw;
	}

	int nxt(int x, int l, int r, int p){
		if (!x) return INF;
		if (l == r) return (p != l) ? l : INF;
		int mid = l+r >> 1;
		if (p > mid) return nxt(a[x].rc, mid+1, r, p);
		else{
			int tp = nxt(a[x].lc, l, mid, p);
			return (tp != INF) ? tp : nxt(a[x].rc, mid+1, r, p);
		}
	}
	
	int nxt(int x, int p) {return nxt(rt[x], 1, n, p);}
	void ins(int x, int to) {ins(rt[x], 1, n, to);}
	void merge(int x, int y) {rt[x] = merge(rt[x], rt[y], 1, n);}
}

namespace ST{
	static const int N = M << 1;
	int n, D, pre[N][20], mx[N], stp[N], lf[N];
	struct vec{
		int g[N], te;
		struct edge{int y, nxt;}e[N];
		inline void push(int x, int y) {e[++te] = (edge){y, g[x]}; g[x] = te;}
		inline int& operator () (int x) {return g[x];}
		inline edge& operator [] (int x) {return e[x];}
	}e;

	void init(int x){
		if (lf[x]) Seg::ins(x, lf[x]);
		int p, y;
		for (p=e(x); p; p=e[p].nxt)
		if ((y=e[p].y) != pre[x][0]){
			init(y);
			Seg::merge(x, y);
		}
	}

	void build(){
		D = (int)log2(n);
		rep (j, 1, D)
		rep (i, 1, n) pre[i][j] = pre[pre[i][j-1]][j-1];
		init(1);
	}

	int getmx(int x){
		int p = Seg::nxt(x, 0);
		int r = p + stp[x] - 1;
		int y;
		per (t, D, 0) if (y = pre[x][t]) {
			int l = Seg::nxt(y, p) + stp[y] - 1;
			if (l > r) x = y;	
		}
		return mx[pre[x][0]];
	}
	
	void dfs(int x){
		int p, y;

		mx[x] = (x == 1) ? 0 : 1 + getmx(x);

		for (p=e(x); p; p=e[p].nxt)
		if ((y=e[p].y) != pre[x][0]) dfs(y);
	}

	int solve(){
		dfs(1);
		
		int res = 0;
		rep (i, 1, n) res = max(res, mx[i]);
		return res;
	}

	inline void link(int x, int y) {e.push(x, y); pre[y][0] = x;}
}

namespace Sam{
	static const int N = M << 1;
	int fa[N], stp[N], ch[N][26], lf[N];
	int last, tot;

	inline int newnode(int l){
		stp[++tot] = l;
		return tot;
	}

	int ext(int p, int q, int c){
		int nq = newnode(stp[p] + 1);
		fa[nq] = fa[q], fa[q] = nq;
		memcpy(ch[nq], ch[q], sizeof ch[q]);
		for (; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
		return nq;
	}

	int sam(int p, int c){
		int np = newnode(stp[p] + 1);
		for (; p && ch[p][c] == 0; p = fa[p]) ch[p][c] = np;
		if (!p) fa[np] = 1;
		else{
			int q = ch[p][c];
			fa[np] = (stp[p] + 1 == stp[q]) ? q : ext(p, q, c);
		}
		return np;
	}

	void build(){
		last = tot = 1;
		per (i, n, 1) lf[last = sam(last, s[i]-'a')] = i;
	}

	void toST(){
		ST::n = Seg::n = tot;
		rep (i, 1, tot) ST::stp[i] = stp[i], ST::lf[i] = lf[i];
		rep (i, 2, tot) ST::link(fa[i], i);
	}
}

int main(){
	
	scanf("%d%s", &n, s+1);

	Sam::build();
	Sam::toST();
	ST::build();
	int ans = ST::solve();
	
	printf("%d\n", ans);

	return 0;
}
posted @ 2017-09-18 20:17  _zwl  阅读(167)  评论(0编辑  收藏  举报