最小表示法
1. 最小表示法原理
问题:给定一个字符串,我们可以任意旋转该字符串,返回字典序最小的字符串表示。
-
假设给定我们的字符串是
s
,首先我们将s
复制一遍接到s
的尾部,假设字符串长度为n
,当我们枚举起点start
在返回0~n-1
时就可以得到所有字符串的表示。 -
初始让
i=0, j=1
,比较s[i+k]、s[j+k]
,用k
表示相对于i、j
的偏移位置,如果相等的话让k++
,直到比较到第一个不相等的位置,假设s[i+k]>s[j+k]
,则说明对于起点在i~i+k
的字符串都不是原串的最小表示,可以更新i
为i+k+1
;反之可以更新j
。 -
更新过程中如果
i==j
,则需要让两者错开,可以让i++
即可,最后返回min(i, j)
即可,这个位置就是字符串对应的最小表示的起点。
2. AcWing上的最小表示法题目
AcWing 158. 项链
问题描述
-
问题链接:AcWing 158. 项链
分析
- 求出两个串的最小表示,比较是否相等即可。
代码
- C++
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2000010;
int n;
char a[N], b[N];
int get_min(char s[]) {
int i = 0, j = 1;
while (i < n && j < n) {
int k = 0;
while (k < n && s[i + k] == s[j + k]) k++;
if (s[i + k] > s[j + k]) i += k + 1;
else j += k + 1;
if (i == j) i++;
}
int k = min(i, j);
s[k + n] = 0;
return k;
}
int main() {
scanf("%s%s", a, b);
n = strlen(a);
memcpy(a + n, a, n);
memcpy(b + n, b, n);
int x = get_min(a), y = get_min(b);
if (strcmp(a + x, b + y)) puts("No");
else {
puts("Yes");
puts(a + x);
}
return 0;
}
3. 力扣上的最小表示法题目
Leetcode 0796 旋转字符串
题目描述:Leetcode 0796 旋转字符串
分析
-
本题的考点:最小表示法。
-
求出两个串的最小表示,比较是否相等即可。
-
假设给定我们的字符串是
s
,首先我们将s
复制一遍接到s
的尾部,假设字符串长度为n
,当我们枚举起点start
在返回0~n-1
时就可以得到所有字符串的表示。 -
初始让
i=0, j=1
,比较s[i+k]、s[j+k]
,用k
表示相对于i、j
的偏移位置,如果相等的话让k++
,直到比较到第一个不相等的位置,假设s[i+k]>s[j+k]
,则说明对于起点在i~i+k
的字符串都不是原串的最小表示,可以更新i
为i+k+1
;反之可以更新j
。 -
更新过程中如果
i==j
,则需要让两者错开,可以让i++
即可,最后返回min(i, j)
即可,这个位置就是字符串对应的最小表示的起点。
代码
- C++
class Solution {
public:
bool rotateString(string A, string B) {
if (A.size() != B.size()) return false;
return get_min(A) == get_min(B);
}
string get_min(string s) {
int n = s.size();
s = s + s;
int i = 0, j = 1;
while (i < n && j < n) {
int k = 0;
while (k < n && s[i + k] == s[j + k]) k++;
if (s[i + k] > s[j + k]) i += k + 1;
else j += k + 1;
if (i == j) i++;
}
int k = min(i, j);
return s.substr(k, n);
}
};
- Java
class Solution {
public boolean rotateString(String A, String B) {
if (A.length() != B.length()) return false;
return get_min(A).equals(get_min(B));
}
private String get_min(String s) {
int n = s.length();
StringBuilder sb = new StringBuilder();
sb.append(s).append(s);
char[] cs = sb.toString().toCharArray();
int i = 0, j = 1;
while (i < n && j < n) {
int k = 0;
while (k < n && cs[i + k] == cs[j + k]) k++;
if (cs[i + k] > cs[j + k]) i += k + 1;
else j += k + 1;
if (i == j) i++;
}
int k = Math.min(i, j);
return sb.substring(k, k + n).toString();
}
}
时空复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),
n
为字符串长度。 -
空间复杂度: O ( n ) O(n) O(n)。