[ARC060D] 最良表現

题目

  点这里看题目。

分析

  由于 KMP 的失配数组有着天然的找循环节的功能,因此我们不难想到对原串进行两次 KMP ,一正一反。
  可以发现如下的规律:
  1. 原串无循环节,这个时候 " 全场最佳 " 只会有一个元素,并且只有一个(即原串本身);
  2. 原串存在循环节,并且仅由一个字符循环而成,此时唯一的 " 亮眼表现 " 就是将原串拆分成字符;
  3. 原串存在循环节。可以发现, " 全场最佳 " 只会有两个元素(在一个循环内部进行划分)。我们只需要统计一下划分方式。可以发现,划分方案不会超过(|S|-1)个((mod 10^9+7)都是骗人的)。我们枚举一下划分位置,并检查一下划分出来的两段有没有循环节即可。(不能直接算 qwq , WA 哭了......)
  时间是(O(|S|))

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

const int MAXN = 5e5 + 5;

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

char S[MAXN];
int nxt1[MAXN], nxt2[MAXN];
int N;

void calc( int *ret )
{
	ret[1] = 0;
	for( int i = 2, j = ret[1] ; i <= N ; i ++ )
	{
		while( j && S[i] ^ S[j + 1] ) j = ret[j];
		if( S[j + 1] == S[i] ) j ++;
		ret[i] = j;
	}
}

bool chkPre( const int p ) { return nxt1[p] && p % ( p - nxt1[p] ) == 0; }
bool chkSuf( const int p ) { return nxt2[p] && p % ( p - nxt2[p] ) == 0; }
bool chk( const int p ) { return chkPre( p ) == 0 && chkSuf( N - p ) == 0; }

int main()
{
	scanf( "%s", S + 1 );
	N = strlen( S + 1 );
	calc( nxt1 ), std :: reverse( S + 1, S + 1 + N ), calc( nxt2 );
	if( nxt1[N] && N % ( N - nxt1[N] ) == 0 )
	{
		if( nxt1[N] == N - 1 ) { printf( "%d
%d
", N, 1 ); return 0; }
		puts( "2" ); int ans = 0;
		for( int i = 1 ; i < N ; i ++ ) 
			ans += chk( i );
		write( ans ), putchar( '
' );
	}
	else puts( "1
1" );
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/12875801.html