zoukankan      html  css  js  c++  java
  • cf 700

    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
    ", ans);
    
    	return 0;
    }
    
  • 相关阅读:
    牛券
    探险
    雷达安装
    智力大冲浪
    奶牛玩杂技
    BJWC2008 秦腾与教学评估
    JSOI2010 部落划分
    作诗
    ASP.NET MVC4系列验证机制、伙伴类共享源数据信息(数据注解和验证)
    正则表达式
  • 原文地址:https://www.cnblogs.com/acha/p/7544942.html
Copyright © 2011-2022 走看看