[CF1063F]String Journey

题目

点这里看题目。

分析

以下标记子串的方法为: \(S[l,r]\) 表示 \(S\) 中从 \(l\)\(r\) 的字符组成的子串。用 ( 表示开区间, [ 表示闭区间。

我们不难想到一个 DP :

\(f(i,k)\):以 \(i\) 开始的后缀,结尾字符串长度为 \(k\) 时的最长的 \(\mathcal{Journey}\)

然后观察并且感性理解一下就会发现,一个最优 \(\mathcal{Journey}\) 里面,一定\(t_i\) 仅仅比 \(t_{i+1}\) 多一个字符

显然我们对于任意一个合法的 \(\mathcal{Journey}\) 都可以经过调整满足这个性质,而满足这个性质就会让 \(\mathcal{Journey}\) 更短,因而会更有机会变长。

你就感性一下就好

于是重新定义状态有:

\(f(i)\):以 \(i\) 开始的后缀中最长的 \(\mathcal{Journey}\)

考虑怎么转移。我们可以枚举一个 \(j\) 。如果说可以转移,那么此时 \(f(i)=f(j)+1\) ,且应该有:

\[S[i,i+f(i)-1)=S[j,j+f(j))\ \lor\ S[i+1,i+f(i))=S[j,j+f(j)) \]

也就是说, \(S[j,j+f(j))\)\(S[i,i+f(i))\) 的子串。

这样做可以用 Hash 方法优化到 \(O(n^2)\)

进一步考虑这个问题。注意一下 \(f(i)\)\(f(i+1)\) 的关系:

StringJourney.png

可以发现 \(f(i)\le f(i+1)+1\) ,否则 \(f(i+1)\) 就应该更大。

假如我们用一个指针 \(cur\) 来标记我们现在可以转移的边界。那么 \(cur\) 就应该是单调不增的。

如果说当前位置为 \(f(i)\) ,而我们发现 \(f(i)=cur-i+1\) 的时候没有办法转移,我们就需要 cur --

如何判断这个东西呢?转移位置 \(j\) 合法需要满足下列条件:

  1. \(s[i,i+f(i)-1)=s[j,j+f(i)-1)\ \lor\ s[i+1,i+f(i))=s[j,j+f(i)-1)\)
  2. \(f(j)\ge f(i)-1\)

考虑第一个条件可以拆成两个串分别判断。此时应该有 \(s[i,i+f(i)-1)\)\(s[j,n]\) 的前缀。我们可以理解为,对原串构建后缀自动机,满足第一个条件的所有 \(j\)其后缀 \(s[j,n]\) 必然在 \(s[i,i+f(i)-1)\) 的 fail 子树里面。

考虑第二个条件。结合前面的分析,我们可以将 \(f(j)\) 的值挂到 \(s[j,j+f(j))\) 对应的状态上,并且只需要查询 \(s[i,i+f(i)-1)\) 在 fail 树内的子树最大值进行判断便可

综合来说,时间复杂度是 \(O(n\log_2n)\)

代码

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

const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 5, MAXLOG = 21;

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;
}

struct edges
{
	int to, nxt;
}Graph[MAXN];

int seg[MAXN << 2];

int f[MAXN];

int fath[MAXN][MAXLOG];
int head[MAXN], DFN[MAXN], siz[MAXN];

int ch[MAXN][26], fa[MAXN], mx[MAXN], ed[MAXN];

int N, rt, tot, lst, ID, lg2, cnt;
char S[MAXN];

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 insert( const char c )
{
	int x = c - 'a', cur = ++ tot, p = lst;
	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[cur] = fa[q] = nq;
	while( p && ch[p][x] == q ) ch[p][x] = nq, p = fa[p];
}

void DFS( const int u )
{
	siz[u] = 1, DFN[u] = ++ ID;
	for( int i = head[u], v ; i ; i = Graph[i].nxt )
		DFS( v = Graph[i].to ), fath[v][0] = u, siz[u] += siz[v];
}

void upt( const int x ) { seg[x] = MAX( seg[x << 1], seg[x << 1 | 1] ); }

void build( const int x, const int l, const int r )
{
	if( l > r ) return ; seg[x] = -INF;
	if( l == r ) return ;
	int mid = l + r >> 1;
	build( x << 1, l, mid );
	build( x << 1 | 1, mid + 1, r );
}

void update( const int x, const int l, const int r, const int pos, const int v )
{
	if( l == r ) { seg[x] = MAX( seg[x], v ); return ; }
	int mid = l + r >> 1;
	if( pos <= mid ) update( x << 1, l, mid, pos, v );
	else update( x << 1 | 1, mid + 1, r, pos, v );
	upt( x ); 
}

int query( const int x, const int l, const int r, const int segL, const int segR )
{
	if( segL <= l && r <= segR ) return seg[x];
	int mid = l + r >> 1, ret = -INF;
	if( segL <= mid ) ret = MAX( ret, query( x << 1, l, mid, segL, segR ) );
	if( mid < segR ) ret = MAX( ret, query( x << 1 | 1, mid + 1, r, segL, segR ) );
	return ret;
}

void init()
{
	for( int i = 2 ; i <= tot ; i ++ ) addEdge( fa[i], i );
	DFS( 1 ), build( 1, 1, tot );
	lg2 = log2( tot );
	for( int j = 1 ; j <= lg2 ; j ++ )
		for( int i = 1 ; i <= tot ; i ++ )
			fath[i][j] = fath[fath[i][j - 1]][j - 1];
}

int locate( const int st, const int len )
{
	int p = ed[st];
	if( mx[p] < len ) return -1;
	for( int i = lg2 ; ~ i ; i -- )
		if( fath[p][i] && mx[fath[p][i]] >= len )
			p = fath[p][i];
	return p;
}

bool chk( const int st, const int len )
{
	int p, val;
	if( len == 1 ) return true;
	if( ~ ( p = locate( st, len - 1 ) ) )
	{
		val = query( 1, 1, tot, DFN[p], DFN[p] + siz[p] - 1 );
		if( val >= len - 1 ) return true;
	}
	if( ~ ( p = locate( st + 1, len - 1 ) ) )
	{
		val = query( 1, 1, tot, DFN[p], DFN[p] + siz[p] - 1 );
		if( val >= len - 1 ) return true;
	}
	return false;
}

int main()
{
	read( N ), scanf( "%s", S + 1 );
	rt = lst = ++ tot;
	for( int i = N ; i ; i -- ) insert( S[i] ), ed[i] = lst;
	init();
	
	f[N] = 1; int rig = N, ans = 1;
	for( int i = N - 1 ; i ; i -- )
	{
		while( ! chk( i, rig - i + 1 ) )
		{
			update( 1, 1, tot, DFN[locate( rig, f[rig] )], f[rig] );
			rig --;
		}
		f[i] = rig - i + 1;
		ans = MAX( ans, f[i] );
	}
	write( ans ), putchar( '\n' );
	return 0;
}
posted @ 2020-08-04 12:02  crashed  阅读(135)  评论(0编辑  收藏  举报