定义:给定一个字符串 S[1~n] ,如果我们不断把他的最后一个字符放到开头,最终会得到 n 个字符串,称这 n 个字符串是循环同构的。 这些字符串中字典序最小的一个,称为字符串 S 的最小表示。
例如 S = "abca",那么它的 4 个循环同构字符串为 "abca","aabc”,"caab","bcaa",S 的最小表示为 "aabc"。因为一个与 S 循环同构的字符串可以用该字符串在 S 中的起始下标表示,所以我们就用 B[i] 表示从 i 开始的循环同构字符串,即 S[i ~ n] + S[1 ~ i-1]。一个字符串的最小表示可以在 O(n) 的线性时间内求出。我们首先把 S 复制一份接在它的结尾,得到的字符串记为 SS 。显然, B[i] = SS[i ~ i+n-1]。
最小表示法:
1、初始化 i = 1,j = 2。
2、通过直接向后扫描的方法,比较B[i] 与 B[j] 两个循环同构串:
(1)、如果扫描了 n 个字符后仍然相等,说明 S 只由一种字符构成,任意 B[i] 都是它的最小表示。
(2)、如果在 i + k 和 j + k 处发现不相等:若 SS[i+k] > SS[j+k],令 i = i + k + 1。若此时 i = j,再令 i = i +1。
若 SS[i+k] < SS[j+k],令 j = j + k +1。若此时 i = j,再令 j = j+1。
3、若 i > n,B[j] 为最小表示;若 j > n,B[i] 为最小表示;否则重复第二步。
代码实现:
int n = strlen(s+1);
for(int i = 1; i <= n; i++) s[n+i] = s[i];
int i = 1, j = 2, k;
while(i <= n && j <= n){
for(k = 0; k <= n && s[i+k] == s[j+k]; k++);
if(k == n) break;//S只由一种字符构成,形如"aaaaa"
if(s[i+k] > s[j+k]) {
i = i + k + 1;
if(i == j) i++;
}else {
j = j + k + 1;
if(i == j) j++;
}
}
ans = min(i,j);