【题解】Codeforces 961F. k-substring 后缀数组,枚举

给定字符串 S ( 1 e 6 ) S(1e6) ,求所有中心子串的最大奇border长度,名词解释:

  1. 中心子串:中心位置和原字符串中心位置相同的子串,即原字符串头尾去掉相同数量的字符后剩下的子串。
  2. border:最长的子串,既是真前缀,又是后缀。
  3. 奇border:最长的奇数长度的子串,既是真前缀,又是后缀,不存在输出-1.

f [ i ] f[i] 表示第 i i 个( i i 0 0 开始)中心子串( s [ i . . . n 1 i ] s[i...n-1-i] )的最大奇border的长度.

结论: f [ i ] f [ i + 1 ] + 2 f[i] \le f[i+1]+2 ,证明如下:

因为对于方案 f [ i ] = j f[i]=j ,有 s [ i . . . i + j 1 ] = s [ n i j . . . n 1 i ] s[i...i+j-1]=s[n-i-j...n-1-i]
.
也有 s [ i + 1... i + j 2 ] = s [ n i j + 1... n 2 i ] s[i+1...i+j-2] = s[n-i-j+1...n-2-i]
.
这保证了 f [ i + 1 ] f[i+1] 至少有一个大于 j 2 j-2 的方案
.
f [ i + 1 ] f [ i ] 2 f[i+1]\ge f[i]-2 ,所以结论成立。

然后就可以快乐枚举,边界是 f [ ( n 1 ) / 2 ] f[(n-1)/2]

检查 f [ i ] = = k f[i]==k 时,直接看 l c p ( i , n i j ) > = j lcp(i,n-i-j)>=j 是否成立即可,使用后缀数组。

构建后缀数组复杂度 O ( n l o g n ) O(nlogn) ,枚举复杂度 O ( n ) O(n) ,总复杂度 O ( n l o g n ) O(nlogn)


  1. border的递推以及其它递推过程中,很爱用到这个枚举上限依赖,第一次见到是在前缀函数的复杂度证明。
  2. n&1==0可能不会产生预期的结果,因为==要比&优先。
namespace SA{}
char str[M];
int ans[M];
int main(void)
{
	int n = read();
	scanf("%s", str);
	SA::build(str, n, 128);

	memset(ans, -1, sizeof(ans));
	int lim = (n-1)/2;
	if((n&1)==0 && str[n/2-1]==str[n/2]) ans[lim] = 1;

	for(int i=lim-1; i>=0; --i)
		for(int j=ans[i+1]+2; j>=0; j-=2)
			if(SA::lcp(i, n-i-j)>=j) 
				ans[i]=j, j=-1;

	for(int i=0; i<=lim; ++i)
		printf("%d ",ans[i] );
    return 0;
}
发布了375 篇原创文章 · 获赞 305 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/103036053