阵列中的A和B类型的字符串中有N个字符(每种类型的数量相同)。如果我们只能交换两个相邻字符,那么确保没有两个相邻字符相同的最小交换数是多少? 例如,输入为:
AAAABBBB
交换的最小数量是6,以使阵列ABABABAB。但是你如何解决它的任何输入?我只能想到O(N ^ 2)解决方案。也许某种形式?
答案 0 :(得分:4)
如果我们只需要计算掉期,那么我们可以使用 O(N)来完成。
为简单起见,我们假设N个元素的数组X应该变为ABAB ....
GetCount()
swaps = 0, i = -1, j = -1
for(k = 0; k < N; k++)
if(k % 2 == 0)
i = FindIndexOf(A, max(k, i))
X[k] <-> X[i]
swaps += i - k
else
j = FindIndexOf(B, max(k, j))
X[k] <-> X[j]
swaps += j - k
return swaps
FindIndexOf(element, index)
while(index < N)
if(X[index] == element) return index
index++
return -1; // should never happen if count of As == count of Bs
基本上,我们从左向右运行,如果找到错位元素,则会与abBbbbA** --> abAbbbB**
中的正确元素(例如O(1)
)进行交换。同时,交换被计算为好像将交换相邻元素的序列。变量i
和j
分别用于缓存下一个A
和B
的索引,以确保完成 FindIndexOf的所有调用在O(N)
。
如果我们需要按掉期排序那么我们不能做得比 O(N ^ 2)更好。
粗略的想法如下。让我们考虑一下您的样本:AAAABBBB
。 B
中的一个需要O(N)交换才能到达A B ...位置,另一个B
需要O(N)才能到达ABA B ...位置等等所以我们最后得到O(N ^ 2)。
答案 1 :(得分:2)
观察如果任何解决方案将交换相同字母的两个实例,那么我们可以通过删除该交换找到更好的解决方案,这必然没有效果。因此,最佳解决方案只能交换不同的字母。
让我们将字符串视为字符串中一种字母(任意选择,比如A
)的索引数组。因此,AAAABBBB
将表示为[0, 1, 2, 3]
,而ABABABAB
将表示为[0, 2, 4, 6]
。
我们知道同一个字母的两个实例永远不会交换最佳解决方案。这使我们总能安全地识别A
的第一个(最左侧)实例,其中第一个元素是索引数组,第二个实例是第二个元素,等等。它还告诉我们数组总是按排序顺序排列在最佳解决方案的每一步。
由于最优解的每一步都交换了不同的字母,我们知道我们的索引数组只是通过一次递增或递减一个元素而在每一步中发展。
长度为n = 2k
的初始字符串将具有长度为A
的数组表示k
。最佳解决方案将此数组转换为
ODDS = [1, 3, 5, ... 2k]
或
EVENS = [0, 2, 4, ... 2k - 1]
由于我们知道在一个最佳解决方案中,一个字母的实例并没有相互传递,我们可以得出一个最佳解决方案必须花费min(abs(ODDS[0] - A[0]), abs(EVENS[0] - A[0]))
个交换来将第一个实例放在正确的位置。
通过实现EVENS
或ODDS
选择仅进行一次(每个字母实例不是一次),并在整个数组中求和,我们可以计算所需交换的最小数量为
define count_swaps(length, initial, goal)
total = 0
for i from 0 to length - 1
total += abs(goal[i] - initial[i])
end
return total
end
define count_minimum_needed_swaps(k, A)
return min(count_swaps(k, A, EVENS), count_swaps(k, A, ODDS))
end
请注意count_minimum_needed_swaps
隐含的循环迭代次数为2 * k = n
;它在O(n)
时间内运行。
通过注意count_minimum_needed_swaps
中哪个术语较小,我们也可以分辨出两个目标状态中的哪一个是最优的。
答案 2 :(得分:0)
由于你知道N,你可以简单地编写一个循环来生成没有所需交换的值。
#define N 4
char array[N + N];
for (size_t z = 0; z < N + N; z++)
{
array[z] = 'B' - ((z & 1) == 0);
}
return 0; // The number of swaps
答案 3 :(得分:0)
@Nemo和@AlexD是对的。算法是n ^ 2阶。 @Nemo误解了我们正在寻找一个重新排序,其中两个相邻的字符不相同,所以我们不能使用它,如果A在B之后,它们就会出现故障。
让我们看看掉期的最小数量。
我们不在乎我们的第一个字符是A还是B,因为我们可以应用相同的算法,但是使用A而不是B,反之亦然。因此,假设单词WORD_N
的长度为2N,N As和N Bs,以A开头。(我使用长度2N来简化计算)。
我们要做的是尝试将下一个B右移到此A,而不考虑其他角色的位置,因为这样我们就可以减少重新排序新单词WORD_{N-1}
的问题。让我们假设下一个B不仅仅是在A之后,如果该单词有超过2个字符,因为这样就完成了第一步,我们将问题减少到下一组字符WORD_{N-1}
。
下一个B应该尽可能在最坏的情况下,所以它是在一半的后面,所以我们需要$ N-1 $掉期将这个B放在A之后(可能小于那个) 。然后我们的单词可以缩减为WORD_N = [A B WORD_{N-1}]
。
我们必须执行此算法大多数N-1次,因为最后一个单词(WORD_1
)将已经被排序。执行算法N-1次我们必须做
N_swaps =(N-1)* N / 2。
其中N是初始单词长度的一半。
让我们看看为什么我们可以为WORD_{N-1}
应用相同的算法,也假设第一个单词是A.在这种情况下,重要的是第一个单词应该与已经排序的对中的相同。我们可以确定WORD_{N-1}
中的第一个字符是A,因为它是我们的初始单词中第一个字符旁边的字符,如果是B,则第一个字符只能执行这两个单词之间的交换。或者没有,我们已经WORD_{N-1}
以与WORD_{N}
相同的字符开头,而WORD_{N}
的前两个字符不同,代价是差不多1次。
答案 4 :(得分:-1)
我认为这个答案类似于phs的答案,就在Haskell中。我们的想法是A
'(或B
')的结果指数是已知的,所以我们需要做的就是计算每个起始指数必须移动的距离并总计总和。 / p>
Haskell代码:
Prelude Data.List> let is = elemIndices 'B' "AAAABBBB"
in minimum
$ map (sum . zipWith ((abs .) . (-)) is) [[1,3..],[0,2..]]
6 --output