[CF700E]Cool Slogans

题目

点这里看题目。

分析

由于这个问题与子串相关,那么我们就先把后缀自动机给建出来。

题目条件非常特殊——“出现至少两次”。而 fail 树上一个状态的祖先状态,根据定义,至少会在当前状态中出现一次。我们便可以知道,答案所对应的状态在 fail 树上一定呈祖孙关系

我们可以用倍增法,求出状态 (S) 的祖先上第一个在 (S) 中出现两次以上的状态:先倍增找到最后一个只出现一次的串,它的父亲即是我们所求。我们称这个状态为 (pre(S))

按照这个关系我们就可以设计 DP :

(f(u)):以(u)作为末状态的字符串序列的最大长度。

转移显然:

[f(u)=1+max_{pre(v)=u}f(v) ]

问题转化为了,如何判断祖先状态有没有出现多次。

我们 使用一个暴力的方法——先用线段树合并得到每个点上的 (end-pos) 集合,再进行检查。

对于状态 (S) ,它有祖先状态 (S') 。由于 (S) 在原串出现一次, (S') 就必然会跟着出现,我们就可以只针对某一个特殊的出现位置检查——比如, (S) 的第一个出现位置(fir(S))

接着, (S')(S) 中出现两次以上等价于:

[exists pin end-pos(S'),pin[fir(S)-len(S)+len(S'),fir(S)) ]

即存在 (S') 的出现位置,满足它不属于 (end-pos(S)) ,但包含在了从 (fir(S)) 开始的 (S) 串里面。这个便是在 (end-pos) 上的区间查询问题,可以根据已有线段树信息解决。

综合以上方法,求 (pre) 总时间 (O(nlog_2^2n)) , DP 时间 (O(n)) 。总时间 (O(nlog_2^2n))

代码

#include <cmath>
#include <cstdio>
#include <cstring>

const int MAXN = 4e5 + 5, MAXLOG = 20, MAXS = MAXN * 50;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MAX( const _T a, const _T b )
{
	return a > b ? a : b;
}

template<typename _T>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
}

struct edge
{
	int to, nxt;
}Graph[MAXN << 1];

int f[MAXN][MAXLOG];
bool s[MAXS]; int lch[MAXS], rch[MAXS];
int dp[MAXN], seq[MAXN], ID;
int ch[MAXN][26], fa[MAXN], mx[MAXN], head[MAXN], rot[MAXN], fir[MAXN];
int N, lg2, rt, lst, tot, cnt, siz;
char S[MAXN];

void upt( const int x ) { s[x] = s[lch[x]] | s[rch[x]]; }
void copy( int a, int b ) { fa[a] = fa[b], mx[a] = mx[b], memcpy( ch[a], ch[b], sizeof ch[b] ); }

void addEdge( const int from, const int to )
{
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
	head[from] = cnt;
}

void expand( const char c )
{
	int x = c - 'a', p = lst, cur = ++ tot;
	mx[cur] = mx[lst] + 1, lst = cur;
	while( p && ! ch[p][x] ) ch[p][x] = cur, p = fa[p];
	if( ! p ) { fa[cur] = rt; return ; }
	int q = ch[p][x];
	if( mx[q] == mx[p] + 1 ) { fa[cur] = q; return ; }
	int nq = ++ tot; copy( nq, q );
	mx[nq] = mx[p] + 1, fa[q] = fa[cur] = nq;
	while( p && ch[p][x] == q ) ch[p][x] = nq, p = fa[p];
}

void update( int &u, const int l, const int r, const int pos )
{
	if( ! u ) u = ++ siz;
	if( l == r ) { s[u] = true; return ; }
	int mid = l + r >> 1;
	if( pos <= mid ) update( lch[u], l, mid, pos );
	else update( rch[u], mid + 1, r, pos );	
	upt( u );
}

bool query( const int u, const int l, const int r, const int segL, const int segR )
{
	if( ! u ) return false;
	if( segL <= l && r <= segR ) return s[u];
	int mid = l + r >> 1, ret = false;
	if( segL <= mid ) ret |= query( lch[u], l, mid, segL, segR );
	if( mid < segR ) ret |= query( rch[u], mid + 1, r, segL, segR );
	return ret;
}

int merg( int u, int v )
{
	if( ! u || ! v ) return u + v;
	int cur = ++ siz;
	lch[cur] = merg( lch[u], lch[v] );
	rch[cur] = merg( rch[u], rch[v] );
	s[cur] = s[u] | s[v];
	return cur;
}

void DFS( const int u )
{
	seq[++ ID] = u;
	for( int i = head[u], v ; i ; i = Graph[i].nxt )
	{
		DFS( v = Graph[i].to );
		rot[u] = merg( rot[u], rot[v] );
		fir[u] = MIN( fir[u], fir[v] );
	}
}

int main()
{
	rt = lst = ++ tot;
	memset( fir, 0x3f, sizeof fir );
	read( N ), scanf( "%s", S + 1 );
	for( int i = 1 ; i <= N ; i ++ ) 
		expand( S[i] ), 
		update( rot[lst], 1, N, i ), fir[lst] = i;
	for( int i = 2 ; i <= tot ; i ++ ) addEdge( fa[i], i );
	DFS( rt );
	lg2 = log2( tot );
	for( int i = 1 ; i <= tot ; i ++ ) f[i][0] = fa[i];
	for( int j = 1 ; j <= lg2 ; j ++ )
		for( int i = 1 ; i <= tot ; i ++ )
			f[i][j] = f[f[i][j - 1]][j - 1];
	for( int i = tot, u, p ; i ; i -- )
	{
		u = p = seq[i];
		for( int k = lg2 ; ~ k ; k -- )
			if( f[p][k] > rt && ! query( rot[f[p][k]], 1, N, fir[u] - mx[u] + mx[f[p][k]], fir[u] - 1 ) )
				p = f[p][k];
		p = fa[p];
		dp[p] = MAX( dp[p], dp[u] + 1 );
	}
	write( dp[rt] ), putchar( '
' );
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/12995366.html