字符串 最小表示法

定义:

表示:
若对于一个字符串 (S) ,和另一个字符串 (S^prime)
若存在一个 (i) 使得 (S^prime[i...N]S^prime[1...i-1])(S) 相等,那么就说 (S^prime)(S) 的一个表示(也称 (S^prime)(S) 循环同构)。
(S) 的最小表示就是满足上述条件的字典序最小(S^prime)


算法复杂度

我们还是先来看看暴力的复杂度:
(O(n^2))
枚举最小表示的开头,暴力判断是否循环同构。
这种做法的时间主要消耗在比较上,如果是随机数据,则表现良好,但是如果是这种:

[aaaa...aaa ]

每次比较次数将达到 (O(n)) 级别,整个算法就成了 (O(n^2))
而我们接下来的算法复杂度是妥妥的 (O(n))


合理解法

我们考虑两个字符串 (A,B),它们在 (S) 中的起始位置分别为 (i,j) ,且它们的前 (k) 位都相等。
如果 (A[i+k]>B[j+k]) 那么对于任何一个字符串 (T) ,它的开头位于 (i)(i+k) 之间,那么它肯定不会成为最优解(想一想为什么)。
所以我们就可以直接将 (i) 跳至 (i+k+1)
复杂度证明:不会。。。


模板题

Luogu
代码:

/*--------------------------------
--Author: The Ace Bee-------------
--Blog: www.cnblogs.com/zsbzsb----
--This code is made by The Ace Bee
--------------------------------*/
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <ctime>
#include <string>
#define rg register
#define clr(x, y) memset(x, y, sizeof x)
using namespace std;
template < typename T > inline void read(T& s) {
	s = 0; int f = 0; char c = getchar();
	while (!isdigit(c)) f |= (c == '-'), c = getchar();
	while (isdigit(c)) s = s * 10 + (c ^ 48), c = getchar();
	s = f ? -s : s;
}

const int _ = 30010;

int n, L; char s[_], t[_];

inline void solve() {
	scanf("%s", s), L = strlen(s);
	int k = 0, i = 0, j = 1;
	while (k < L && i < L && j < L) {
		int tmp = s[(i + k) % L] - s[(j + k) % L];
		if (tmp == 0) ++k;
		else {
			if (tmp < 0) j += k + 1;
			if (tmp > 0) i += k + 1;
			if (i == j) ++i; k = 0;
		}
	}
	printf("%d
", min(i, j) + 1);
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.in", "r", stdin);
#endif
	read(n);
	for (rg int i = 1; i <= n; ++i) solve();
	return 0;
}
原文地址:https://www.cnblogs.com/zsbzsb/p/11627122.html