最小表示法

OI WIki

找出一个字符串的最小循环同构,就是最小表示法。

由于我们只想要找到最小的,所以可以用打擂台的方式进行筛选。但是如果每个串都逐位比较的话,发现会被 \(aaa\cdots aab\) 这样的数据轻松卡成 \(O(n^2)\)

考虑优化,我们发现如果有两个相同的很长的子串,复杂度就难以保证。但是其实相同的都是可以跳过的。举个例子,你现在比较完了 \([l_1,r_1]\)\([l_2,r_2]\) 这两个区间,其中 \([l_1,r_1-1]\)\([l_2,r_2-1]\) 相同,\(s_{r_1}< s_{r_2}\) ,那么我们没有必要再比较 \([l_1+1,r_1]\)\([l_2+1,r_2]\) 这两个区间,因为显然是前者更小。所以我们可以直接把第二个指针移到 \(r_2\) 这个位置,然后继续算法。

这里是大致代码实现:

int i=1,j=2,k=0;
while(i<=n&&j<=n&&k<n)
	if(a[t(i+k)]==a[t(j+k)])
		k++;
	else
	{
		if(a[t(i+k)]>a[t(j+k)]) i=i+k+1;
		else j=j+k+1;
		k=0;
		if(i==j) i++;
        }
i=min(i,j);

发现 \(k\) 每次增加必然会使得 \(i,j\) 其中一个指针增加,\(else\) 里的语句也必然使得一个指针增加,而指针最多增加 \(2n-1\) 次,所以这个代码的均摊时间复杂度是 \(O(n)\) 的。

posted @ 2020-07-30 13:21  With_penguin  阅读(141)  评论(0编辑  收藏  举报