最小表示法是用于解决字符串最小表示问题的方法。
当字符串
则称
字符串
考虑对于一对字符串
不妨先考虑
所以我们比较时可以跳过下标
这样,我们就完成了对于上文暴力的优化。
- 初始化指针
$i$ 为$0$ ,$j$ 为$1$ ;初始化匹配长度$k$ 为$0$ - 比较第
$k$ 位的大小,根据比较结果跳转相应指针。若跳转后两个指针相同,则随意选一个加一以保证比较的两个字符串不同 - 重复上述过程,直到比较结束
- 答案为
$i,j$ 中较小的一个
详细代码
// C++ Version
int k = 0, i = 0, j = 1;
while (k < n && i < n && j < n) {
if (sec[(i + k) % n] == sec[(j + k) % n]) {
k++;
} else {
sec[(i + k) % n] > sec[(j + k) % n] ? i = i + k + 1 : j = j + k + 1;
if (i == j) i++;
k = 0;
}
}
i = min(i, j);
# Python Version
k, i, j = 0, 0, 1
while k < n and i < n and j < n:
if sec[(i + k) % n] == sec[(j + k) % n]:
k += 1
else:
if sec[(i + k) % n] > sec[(j + k) % n]:
i = i + k + 1
else:
j = j + k + 1
if i == j:
i += 1
k = 0
i = min(i, j)
[!NOTE] AcWing 158. 项链
题意: TODO
[!TIP] 思路
详细代码
#include <bits/stdc++.h>
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 (k == n)
break;
if (s[i + k] > s[j + k])
i += k + 1;
else
j += k + 1;
if (i == j)
j ++ ;
}
int k = min(i, j);
s[k + n] = 0; // 标记
return k;
}
int main() {
// scanf("%s%s", a, b);
cin >> 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))
cout << "No" << endl;
else {
cout << "Yes" << endl;
cout << a + x << endl;
}
return 0;
}
[!NOTE] AcWing 1410. 隐藏密码
题意: TODO
[!TIP] 思路
详细代码
// 求字符串的最小表示
// 经典算法 背过
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n;
string s;
int get_min() {
// 用两个指针来找
// 枚举从 i / j 开始的两个连续区间
int i = 0, j = 1;
while (i < n && j < n) {
int k = 0;
while (k < n && s[i + k] == s[j + k]) ++ k ;
if (k == n) break;
if (s[i + k] > s[j + k]) i += k + 1;
else j += k + 1;
if (i == j) ++ j ;
}
return min(i, j);
}
int main() {
cin >> n;
string line;
while (cin >> line) s += line;
s += s; // 后面再接一份
cout << get_min() << endl;
return 0;
}
[!NOTE] LeetCode 899. 有序队列
题意: TODO
[!TIP] 思路
简单推导,显然在
k = 1
时是最小表示法其他情况都可以直接搞成全局最小
sorting
详细代码
class Solution {
public:
string orderlyQueue(string s, int k) {
if (k == 1) {
int i = 0, j = 1, n = s.size();
s = s + s;
while (i < n && j < n) {
int k = 0;
while (k < n && s[i + k] == s[j + k])
k ++ ;
if (k == n)
break;
if (s[i + k] > s[j + k])
i += k + 1;
else
j += k + 1;
if (i == j)
j ++ ;
}
int k = min(i, j);
return s.substr(k, n);
}
sort(s.begin(), s.end());
return s;
}
};
[!NOTE] LeetCode 1163. 按字典序排在最后的子串
题意: TODO
[!TIP] 思路
字符串最大表示法
详细代码
class Solution {
public:
int get_max(string s) {
int i = 0, j = 1, n = s.size();
while (i < n && j < n) {
int k = 0;
while (k < n && s[i + k] == s[j + k]) ++ k ;
if (k == n) break;
// `>` ---> `<`
if (s[i + k] < s[j + k]) i += k + 1;
else j += k + 1;
if (i == j) ++ j ;
}
return min(i, j);
}
string lastSubstring(string s) {
return s.substr(get_max(s));
}
};