|
| 1 | +# [899.有序队列](https://leetcode.cn/problems/orderly-queue/) |
| 2 | + |
| 3 | +<p>给定一个字符串 <code>s</code> 和一个整数 <code>k</code> 。你可以从 <code>s</code> 的前 <code>k</code> 个字母中选择一个,并把它加到字符串的末尾。</p> |
| 4 | + |
| 5 | +<p>返回 <em>在应用上述步骤的任意数量的移动后,字典上最小的字符串 </em>。</p> |
| 6 | + |
| 7 | +<p> </p> |
| 8 | + |
| 9 | +<p><strong>示例 1:</strong></p> |
| 10 | + |
| 11 | +<pre> |
| 12 | +<strong>输入:</strong>s = "cba", k = 1 |
| 13 | +<strong>输出:</strong>"acb" |
| 14 | +<strong>解释:</strong> |
| 15 | +在第一步中,我们将第一个字符(“c”)移动到最后,获得字符串 “bac”。 |
| 16 | +在第二步中,我们将第一个字符(“b”)移动到最后,获得最终结果 “acb”。 |
| 17 | +</pre> |
| 18 | + |
| 19 | +<p><strong>示例 2:</strong></p> |
| 20 | + |
| 21 | +<pre> |
| 22 | +<strong>输入:</strong>s = "baaca", k = 3 |
| 23 | +<strong>输出:</strong>"aaabc" |
| 24 | +<strong>解释: |
| 25 | +</strong>在第一步中,我们将第一个字符(“b”)移动到最后,获得字符串 “aacab”。 |
| 26 | +在第二步中,我们将第三个字符(“c”)移动到最后,获得最终结果 “aaabc”。 |
| 27 | +</pre> |
| 28 | + |
| 29 | +<p> </p> |
| 30 | + |
| 31 | +<p><strong>提示:</strong></p> |
| 32 | + |
| 33 | +<ul> |
| 34 | +<li><code>1 <= k <= S.length <= 1000</code></li> |
| 35 | +<li><code>s</code> 只由小写字母组成。</li> |
| 36 | +</ul> |
| 37 | + |
| 38 | + |
| 39 | +<details> |
| 40 | +<summary>标签:</summary> |
| 41 | +['数学', '字符串', '排序'] |
| 42 | +</details> |
| 43 | + |
| 44 | +<details> |
| 45 | +<summary>难度:Hard</summary> |
| 46 | +喜欢:82 |
| 47 | +</details> |
| 48 | + |
| 49 | + |
| 50 | +---------- |
| 51 | + |
| 52 | +# 字符串的最小表示法 |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | +已知字符串 $S=bacacabc$ ,求 $S$ 的最小表示法。 |
| 57 | + |
| 58 | + |
| 59 | + |
| 60 | +定义 $S[i, j]$ 表示 下标 从 $i$ 到 $j$ 的子串; |
| 61 | + |
| 62 | +定义**同构字符串** $B[i] = S[i,...,n] + S[1,...,i-1]$; |
| 63 | + |
| 64 | +我们使用开二倍空间的方式来处理环形问题。将 $S$ 复制一份 接到尾部 $SS = bacacabcbacacabc$,则 $B[i] = SS[i, i+n-1]$ |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | +比较过程:$i=2,j=4, k=3$ |
| 69 | + |
| 70 | +当 $SS[i+k] = SS[j+k]$ 时, $k++$; |
| 71 | + |
| 72 | +当 $SS[j+k] < SS[i+k]$ 时,$B[j]$ 为更小的表示法,同样,$\forall_{1<=p<k} B[p]$ 都不为字符串的最小表示法,因为对于 $\forall_{1<=p<k} B[i+p]$ 都存在对应的一个$\forall_{1<=p<k} B[j+p]$ 且 $B[j+p]$ 更小;所以 $i = i+k+1$ ;如果此时 $i=j$,则 $B[i]$和 $B[j]$ 表示同一字符串, 需要将 $i$ 移到下一位,$i++$; |
| 73 | + |
| 74 | +当 $SS[j+k] > SS[i+k]$ 时,处理方法同上; |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | + |
| 81 | + |
| 82 | +## 核心代码实现 |
| 83 | + |
| 84 | + |
| 85 | + |
| 86 | +```cpp |
| 87 | +int n = strlen(s + 1); |
| 88 | +for (int i = 1; i <= n; i++) s[n+i] = s[i]; |
| 89 | +int i = 1, j = 2, k; |
| 90 | +while (i <= n && j <= n) { |
| 91 | + for (k = 0; k < n && s[i+k] == s[j+k]; k++); |
| 92 | + if (k == n) break; |
| 93 | + if (s[i+k] > s[j+k]) { |
| 94 | + i = i + k + 1; |
| 95 | + if (i == j) i++; |
| 96 | + } else { |
| 97 | + j = j + k + 1; |
| 98 | + if (i == j) j++; |
| 99 | + } |
| 100 | +} |
| 101 | +ans = min(i, j); |
| 102 | +``` |
| 103 | + |
| 104 | + |
| 105 | + |
| 106 | +## (分情况讨论) $O(n)$ |
| 107 | + |
| 108 | + |
| 109 | + |
| 110 | +当k=1时,用字符串的最小表示法求解; |
| 111 | + |
| 112 | +当 k>=2时,可以移动成任意最小字符串;比如edcba,可以转换成abcde,可以自己转换证明; |
| 113 | + |
| 114 | + |
| 115 | + |
| 116 | +## 时间复杂度 |
| 117 | + |
| 118 | +$O(n)$ |
| 119 | + |
| 120 | +## 代码实现 |
| 121 | + |
| 122 | +```java [] |
| 123 | +class Solution { |
| 124 | + public String orderlyQueue(String s, int k) { |
| 125 | + if (k == 1) { |
| 126 | + String res = s; |
| 127 | + for (int i = 0; i < s.length(); i++) { |
| 128 | + s = s.substring(1) + s.charAt(0); |
| 129 | + if (s.compareTo(res) < 0) res = s; |
| 130 | + } |
| 131 | + return res; |
| 132 | + } |
| 133 | + char[] ss = s.toCharArray(); |
| 134 | + Arrays.sort(ss); |
| 135 | + return new String(ss); |
| 136 | + } |
| 137 | +} |
| 138 | +``` |
| 139 | + |
| 140 | +```cpp [] |
| 141 | + |
| 142 | +``` |
| 143 | + |
| 144 | +## 参考文献 |
| 145 | + |
| 146 | + |
| 147 | + |
| 148 | +## 相似题目 |
| 149 | + |
| 150 | + |
| 151 | + |
| 152 | +### [158. 项链](https://www.acwing.com/problem/content/160/) |
| 153 | + |
| 154 | + |
| 155 | + |
| 156 | +判定 两个字符串 $S, T$ 是否**同构**,可以通过比较其 **最小表示法** 是否相等实现 |
| 157 | + |
| 158 | + |
| 159 | + |
| 160 | +```cpp |
| 161 | +#include <iostream> |
| 162 | +#include <cstring> |
| 163 | +#include <algorithm> |
| 164 | +using namespace std; |
| 165 | + |
| 166 | +const int N = 2000010; |
| 167 | +char a[N], b[N]; |
| 168 | + |
| 169 | +int dict_min(char s[]){ |
| 170 | + int n = strlen(s + 1); |
| 171 | + for (int i = 1; i <= n; i ++ ) s[n+i] = s[i]; |
| 172 | + int i = 1, j = 2, k; |
| 173 | + while (i<=n && j <= n){ |
| 174 | + for ( k = 0; k < n && s[i+k] == s[j+k]; k ++ ); |
| 175 | + if (k ==n) break; |
| 176 | + if (s[i+k] > s[j+k]) { |
| 177 | + i = i+k+1; |
| 178 | + if (i==j) i++; |
| 179 | + } else { |
| 180 | + j = j+k+1; |
| 181 | + if (i==j) j++; |
| 182 | + } |
| 183 | + } |
| 184 | + return min(i, j); |
| 185 | +} |
| 186 | + |
| 187 | +int main() |
| 188 | +{ |
| 189 | + scanf("%s", a + 1); |
| 190 | + int n = strlen(a+1), x = dict_min(a); |
| 191 | + scanf("%s", b+1); |
| 192 | + int m = strlen(b+1), y = dict_min(b); |
| 193 | + a[x + n] = b[y + m] = 0; |
| 194 | + if (m ==n && !strcmp(a +x, b+y)) { |
| 195 | + cout << "Yes" << endl; |
| 196 | + puts(a+x); |
| 197 | + }else cout << "No" << endl; |
| 198 | + return 0; |
| 199 | +} |
| 200 | +``` |
| 201 | +
|
| 202 | +
|
| 203 | +
|
0 commit comments