CF1063F

CF1063F

tag:Suffix Tree,Dp

定义字符串数组 \(t_{1\dots k}\) 合法当且仅当 \(t_{i}\)\(t_{i-1}\) 的字串且 \(t_i\ne t_{i-1}\)

给定字符串 \(s\),求最大的 \(k\) 使得可以将 \(s\) 提取出 \(k\) 个不相交的区间,依次排序后得到的字符串数组合法。

\(|s|\le 5\times 10^5\)

Solution

最优决策下我们必然让长度单调递减,每次恰好减 \(1\)

枚举区间 \([i,j]\),然后连边,我们可以做到 \(\mathcal O(n^3)\) 的连边,然后就是一个 DAG 最长路的问题了。

进一步观察,我们每个串可以用后缀树上一个节点 \(\rm +~end\) 的位置表示。

注意到长度上界至多为 \(1+2+...+cnt=|s|\),即 \(cnt=\sqrt{|s|}\),于是我们可以得到一个 \(\mathcal O(|s|\sqrt{|s|})\) 的做法。

实际上最优决策是固定的,每种字符都会被固定在一个合法的最右点。

好像非常有道理:

\(f_i\) 表示以 \(i\) 为结尾最长合法的字符串数组。

直觉上从后往前让 \(f_i\) 增加就好了。

于是转移为 \(f_i=\max(f_{j})+1,j\ge i+f_i,s_{j-f_j+1...j}\subseteq s_{i-f_i+1...i}\)

接下来可以证明 \(f_{i}\ge f_{i+1}-1\)

\(i+1\) 的结尾删去之后显然 \(f_i\) 是合法的。

于是不难得到做法,从后往前 Dp,每个 \(f_i\)\(f_{i+1}-1\) 开始枚举长度 \(L\),那么只需要 check,对于长度 \(L\),我们只需要检查是否存在字符 \([i+L,n]\)

然后可以考虑在移动的时候暴力维护 \(r\) 的移动...大概就没了吧?

然而仔细做了一下,这样貌似非常不好做...

于是只能改成 Dp 状态,设为 \(f_i\) 表示以 \(i\) 为开头的最长的答案。

不难得到 \(f_{i+1}\ge f_i-1\),于是得到 \(f_i\le f_{i+1}+1\)

于是需要 check 的区间就是 \(i+f_i\) 这段区间,不难注意到这个值是不增的,所以直接扫就可以了!

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int N = 1e6 + 5 ; 
const int inf = 1e9 + 7 ; 
const int Inf = 1e8 + 7 ; 
struct SuffixTree {
	int lef, len, lk ; 
	int ch[28] ; 
} t[N] ;
int tr[N << 2], m, n, cnt = 1, bef = 1, rem ; 
int Id[N], idx, L[N], R[N], f[N], fa[N][19], dep[N] ;
char s[N] ;
int node(int l, int len) { t[++ cnt].lk = 1, t[cnt].lef = l, t[cnt].len = len ; return cnt ; } 
int query( int x, int l, int r, int ql, int qr ) {
	if( ql <= l && r <= qr ) return tr[x] ; 
	if( ql > r || qr < l ) return 0 ; 
	int mid = ( l + r ) >> 1 ; 
	return max(query(ls(x), l, mid, ql, qr), query(rs(x), mid + 1, r, ql, qr)) ;
}
void update( int x, int l, int r, int k, int d ) {
	if( l == r ) { tr[x] = d ; return ; }
	int mid = ( l + r ) >> 1 ; 
	if( mid >= k ) update( ls(x), l, mid, k, d ) ; 
	else update( rs(x), mid + 1, r, k, d ) ; 
	tr[x] = max( tr[ls(x)], tr[rs(x)] ) ; 
}
void insert(int x) {
	++ m, s[x] -= 'a' ; int u = s[x], lst = 1 ; ++ rem ; 
	while( rem ) {
		while( rem > t[t[bef].ch[s[m - rem + 1]]].len ) 
		rem -= t[bef = t[bef].ch[s[m - rem + 1]]].len ; 
		int &d = t[bef].ch[s[m - rem + 1]], c = s[t[d].lef + rem - 1] ;
		if( u == c || !d ) {
			t[lst].lk = bef, lst = bef ; 
			if( !d ) d = node( m - rem + 1, inf ) ; 
			else return ;
		} else {
			int np = node(t[d].lef, rem - 1) ;
			t[np].ch[u] = node(m, inf), t[np].ch[c] = d,
			t[d].lef += (rem - 1), t[d].len -= (rem - 1),
			t[lst].lk = d = np, lst = d ; 
		} (bef == 1) ? -- rem : bef = t[bef].lk ;  
	} 
}
void dfs( int x, int ff, int d ) {
	L[x] = ++ idx ; 
	fa[x][0] = ff ; rep( i, 1, 17 ) fa[x][i] = fa[fa[x][i - 1]][i - 1] ; 
	if( t[x].len > Inf ) {
		t[x].len = n + 1 - t[x].lef, d += t[x].len, Id[n - d + 1] = x ; dep[x] = d ; 
		return ; 
	}
	d += t[x].len ; int v ; dep[x] = d ; 
	rep( i, 0, 26 ) if( v = t[x].ch[i] ) dfs( v, x, d ) ; 
	R[x] = idx ; 
}
int Get( int l, int le ) {
	int x = Id[l] ; 
	drep( i, 0, 17 ) if(dep[fa[x][i]] >= le) x = fa[x][i] ; 
	return x ; 
}
signed main()
{
	n = gi(), scanf("%s", s + 1 ), t[0].len = inf, s[++ n] = 'z' + 1 ; 
	rep( i, 1, n ) insert(i) ; -- n ; 
	dfs(1, 1, 0) ; int r = n + 1 ; f[n + 1] = 0 ; t[1].len = 0 ; 
	for( re int i = n; i >= 1; -- i ) {
		for( re int j = f[i + 1] + 1; j > 0; -- j ) {
			int l = i + j ;
			if( r > l ) -- r, update( 1, 1, idx, L[Id[r]], f[r] ) ;
			int u = Get(i, j - 1) ; 
			int mx = query(1, 1, idx, L[u], R[u] ) ; 
			u = Get(i + 1, j - 1) ;
			mx = max( mx, query(1, 1, idx, L[u], R[u]) ) ;
			if( mx >= j - 1 ) { f[i] = j ; break ; }
		}
	}
	int ans = 0 ;
	rep( i, 1, n ) ans = max( ans, f[i] ) ;
	cout << ans << endl ; 
	return 0 ;
}
原文地址:https://www.cnblogs.com/Soulist/p/13709248.html