「最小表示法」学习笔记

算法应用

用于算出与给定字符串循环同构的字典序最小的字符串。

循环同构

对于一个字符串 \(T\),若在字符串 \(S\) 中存在一个位置 \(i\) ,使得:

\(S[i,i+1,...,n] + S[1,2,...,i-1] = T\)

那么我们说字符串 \(S,T\) 循环同构。

算法流程

朴素算法:

枚举两个起始位置 \(i,j\) ,维护一个 \(k\),表示当前比较到哪一位了。

然后分情况:

  • \(s[i+k] \gt s[j+k]\) 时,\(i\) 增大 \(1\)\(k\) 清零;

  • \(s[i+k] \lt s[j+k]\) 时,\(j\) 增大 \(1\)\(k\) 清零;

  • \(s[i+k] = s[j+k]\) 时,\(k\) 增大 \(1\)

这么做的话剩下的字符串(即从 \(\min(i,j)\) 开始的字符串)就是最优解。

但这么做是最差可能退化到 \(O(n^2)\) 的,会被卡掉。

于是考虑优化。

优化:

考虑两个字符串 \(A,B\) ,他们在原串 \(s\) 中的起始位置为 \(i,j\),且前 \(k\) 个字符均相同。

\(s[i+k] \gt s[j+k]\),发现起始位置下标为 \(l (i \leq l \leq i+k)\) 的字符串都不可能最优。

为什么呢?首先我们对于一个起始下标为 \(i + p(p \leq k)\) 的字符串,根据我们前面的条件,可以得到 \(s[i+p,i+p+1,...,i+k] = s[j+p,j+p+1,...,j+k]\) ,而 \(s[i+k]>s[j+k]\),所以选择起始下标为 \(j+p\) 的字符串显然更优于前者。所以 \([i,i+k]\) 为起始下标的字符串我们就直接跳过,\(i\) 从原 \(i + k + 1\) 的位置开始枚举。如果跳过后 \(i=j\),那就随便给其中一个加 \(1\),不然会导致算法提前终止(完全匹配)。

\(s[i+k] \lt s[j+k]\) 的情况同理。这样我们就完成了对朴素算法的优化。

时间复杂度 \(O(n)\)

模板代码

P13270 【模板】最小表示法

#include <bits/stdc++.h>
using namespace std;

int n;
char a[20000011]; 

int main() {
	scanf("%d",&n);
    scanf("%s",a + 1);
	for(int i=1;i<=n;i++)
		a[n + i] = a[i];
	int i = 1,j = 2,k = 0;
	while (i <= n && j <= n && k < n) {
		if (a[i + k] == a[j + k])
			k++;
		else {
			if (a[i + k] > a[j + k])
				i += k + 1;
			else
				j += k + 1;
			if (i == j)
				j++;
			k = 0;
		}
	}
	k = min(i,j);
	for(int i=1;i<=n;i++)
		printf("%c",a[k + i - 1]);
	return 0;
}

例题

P10476 Necklace

做法很板,对于输入的两个字符串,如果它们循环同构的话,那它们就是一样的。对两个分别算最小表示再对比即可。


点个赞吧求求了

posted @ 2026-08-09 13:51  DWBbobo  阅读(4)  评论(0)    收藏  举报