【发布时间】:2014-05-15 14:26:20
【问题描述】:
问题陈述:
找到将一个字符串转换成另一个相同长度的字符串的最小交换次数,这可能有也可能没有重复的字符;允许任意交换。
min_swaps('kamal', 'amalk') -> 3
# 1 2 3
# kamal -> lamak -> aamlk -> amalk
注意:关于 SO 有很多这样的问题,但似乎没有一个适用于任意交换。
初步方法:
let s1 = 'kamal'
let s2 = 'amalk'
假设 s1 是“正确的”排序,即它的元素从0 -> N-1 以递增的顺序映射到序列。
0 1 2 3 4
k a m a l
现在创建一个数组 P,它是从 s2 中的字母到 s1 中正确索引的映射:
1 2 3 4 0
a m a l k
P = [1,2,3,4,0]
现在我们可以使用修改后的归并排序来计算 P 中的数组反转次数,这将给我们提供无序元素的数量。
修改归并排序:
int main(int argc, char ** argv) {
int array[] = { 1,2,3,4,0 };
int array_size = sizeof(array)/sizeof(array[0]);
int inversions = merge_sort(array, 0, array_size - 1);
printf("Found %d inversions\n", inversions);
return 0;
}
int merge_sort(int a[], int start, int end) {
if ( end > start ) {
int mid = start + (end - start) / 2;
int x = merge_sort(a, start, mid);
int y = merge_sort(a, mid + 1, end);
int z = merge(a, start, mid, end);
return x + y + z;
}
return 0;
}
int merge(int a[], int start, int mid, int end) {
int l = start, r = mid + 1;
int i = 0;
int temp[end - start + 1];
int splitInversionCount = 0;
while ( l <= mid && r <= end ) {
if ( a[l] < a[r] ) {
temp[i++] = a[l++];
}
else {
splitInversionCount += mid - l + 1;
temp[i++] = a[r++];
}
}
// copy whichever half of the array remains
while ( l <= mid ) {
temp[i++] = a[l++];
}
while ( r <= end ) {
temp[i++] = a[r++];
}
// copy temp back into a
int k;
for(k = 0; k < i; k++) {
a[k + start] = temp[k];
}
return splitInversionCount;
}
这样做的问题是它给出了仅与相邻元素交换的最小数量,这将返回 4 而不是 3。
问题:
有没有办法扩展这个算法来捕获任意交换?还是我需要一种完全不同的方法?
【问题讨论】:
-
你有时间限制吗?输入大小?
-
这个问题看起来好像是关于任意交换:stackoverflow.com/questions/18292202/…
-
您正在寻找 en.wikipedia.org/wiki/Cayley%27s_theorem 和 en.wikipedia.org/wiki/Cayley_graph 。问题大致相当于魔方解算法。
-
@HunterMcMillen 我刚刚编辑了我的评论,因为我意识到第二个答案实际上是一个非常好的分析。另外,无论如何,如果有人有更好的解决方案,应该在那里添加
-
Subhasis Das 没有。抱歉,我忘记了排序是不确定的 :)