最小表示法

\(\text{luogu-13270}\)

最小表示板子。

若长度为 \(n\) 的字符串 \(s\) 中可以选择一个位置 \(i\),使得 \(\overline{s_i\cdots s_ns_1\cdots s_{i-1}}=t\),则称 \(s\)\(t\) 循环同构。字符串 \(s\)最小表示为与 \(s\) 循环同构的所有字符串中字典序最小的字符串。

给定一个长度为 \(n\) 的字符串 \(s\),请求出 \(s\) 的最小表示。


首先考虑暴力,枚举循环同构串的第一个位置,然后暴力比较,\(O(n^2)\)

我们可以优化暴力,将字符串先拼一个串到末尾,可以预处理哈希值,然后用二分找到第一个不相等的位置,看是否更新为此位置即可,\(O(n \log n)\)

考虑接着优化,假设目前要比较 \(mk\) 为开头和 \(i\) 为开头的字符串,首先暴力找到一个 \(r < n\) 使得 \(s_{mk \sim mk + r - 1}\)\(s_{i \sim i + r - 1}\) 相等,\(s_{mk+r}\)\(s_{i+r}\) 不相等。如果没有合适的 \(r\) 说明 \(s\) 中所有字符相等。

如果 \(s_{mk+r}<s_{i+r}\),说明对于所有 \(0 \le k \le r\),以 \(i+k\) 开头的字符串不可能成为最优解,此时直接更新 \(i \gets i+r+1\) 即可。

否则说明 \(s_{i+r}\) 更优,直接更新 \(i \gets \max(i+1,mk+j+1)\) 即可。

时间复杂度证明:

对于 \(s_{mk+r}<s_{i+r}\),通过 \(O(r)\) 的枚举使 \(i\) 前进了 \(r\) 步,最多移动 \(n\) 步,时间复杂度均摊 \(O(n)\)

对于 \(s_{mk+r}>s_{i+r}\)\(mk\)\(i\) 总共移动了至少 \(r\) 步,最多移动 \(n\) 步,时间复杂度均摊 \(O(n)\)

所以总时间复杂度为 \(O(n)\)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

long long ls;
string s;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> ls >> s; s = " " + s + s;
	long long mk = 1;
	for(int i = 2; i <= ls; ) {
		long long r = 0;
		while(r < ls && s[mk + r] == s[i + r]) r ++;
		if(r == ls) break;
		if(s[mk + r] > s[i + r]) {
			long long res = mk;
			mk = i, i = max(i + 1ll, res + r + 1);
		}
		else i += r + 1;
	}
	for(int i = mk; i <= mk + ls - 1; i ++) cout << s[i];
	cout << "\n";
	return 0;
}

\(\text{luogu-1368}\)

把最小表示的字符串换成数组了而已,不卡 \(O(n \log n)\)

不过不想写哈希加二分了,就这样改改交了吧。

\(\text{Necklace}\)

给定两个数字组成的字符串 \(s,t\),先判断两个字符串是否循环同构。

如果不循环同构,则输出 \(\text{No}\),否则先输出 \(\text{Yes}\),第二行输出 \(s\) 的最小表示。

\(1 \le |s| \le 10^6\)


直接处理一下两个串的最小表示,如果两个串的最小表示相等,则易证两个字符串循环同构。

\(\text{hdu-2609}\)

给定 \(n\) 个字符串,判断有多少个不同的字符串,若两个字符串循环同构则看作相同的字符串。

\(2 \le n \le 10^4\)


把每个字符串处理出来最小表示,然后用 \(\text{map}\) 计数即可。

posted @ 2025-11-11 21:57  So_noSlack  阅读(5)  评论(0)    收藏  举报