Как я могу вычислить числовое расстояние между двумя последовательностями при условии, что элементы последовательности можно менять местами только с соседними элементами? Вставка и удаление не допускаются. Функция расстояния должна определять количество операций обмена, необходимых для извлечения одной строки из другой. Более того, функция расстояния не определена для последовательностей разной длины или если последовательности не используют одни и те же символы. Наконец, вы можете предположить, что в последовательности нет дубликатов.
Основываясь на моих исследованиях, мне понадобится что-то вроде расстояния Дамерау–Левенштейна, но я не знаю, как правильно изменить функцию расстояния.
Для последовательности ABC
единственными двумя допустимыми преобразованиями с расстоянием 1
являются BAC
или ACB
.
use std::collections::HashSet;
use std::hash::Hash;
fn dist<T: PartialEq + Eq + Hash>(lhs: &[T], rhs: &[T]) -> Option<usize> {
// Distance function is not defined for differing lengths
if lhs.len() != rhs.len() {
return None;
}
let lhs_c: HashSet<_> = lhs.iter().collect();
let rhs_c: HashSet<_> = rhs.iter().collect();
// Distance function is not defined if an element occurs more than once in each sequence
if lhs.len() != lhs_c.len() || rhs.len() != rhs_c.len() {
return None;
}
// Distance function is not defined if the sequences don't share all elements
if lhs_c != rhs_c {
return None;
}
// Calculate the edit distance of `lhs` and `rhs` with only adjacent transpositions.
todo!()
}
#[test]
fn dist_is_none_for_differing_lengths() {
assert_eq!(None, dist(&["b", "a", "c"], &["a", "b", "c", "d"]));
}
#[test]
fn dist_is_none_with_duplicates() {
assert_eq!(None, dist(&["b", "a", "c", "a"], &["a", "b", "c", "d"]));
assert_eq!(None, dist(&["a", "b", "c", "d"], &["a", "b", "c", "c"]));
}
#[test]
fn dist_is_none_with_non_empty_difference() {
assert_eq!(None, dist(&["a", "b", "c"], &["d", "e", "f"]));
}
#[test]
fn dist_bac_to_abc_is_one() {
assert_eq!(Some(1), dist(&["b", "a", "c"], &["a", "b", "c"]));
}
#[test]
fn dist_bca_to_bac_is_one() {
assert_eq!(Some(1), dist(&["b", "c", "a"], &["b", "a", "c"]));
}
#[test]
fn dist_cab_to_abc_is_two() {
assert_eq!(Some(2), dist(&["c", "a", "b"], &["a", "b", "c"]));
}
#[test]
fn dist_cdab_to_abcd_is_four() {
assert_eq!(Some(4), dist(&["c", "d", "a", "b"], &["a", "b", "c", "d"]));
}
@Jmb, да, твое понимание правильное.
Это то же самое, что количество свопов, необходимое для пузырьковой сортировки одной из последовательностей в другую. См. stackoverflow.com/questions/20035505/… и geeksforgeeks.org/inversion-count-in-array-using-merge-sort
@trueequalsfalse, к сожалению, тип T
не заказан (не реализует PartialOrd
). Я использовал str
только в тестах, чтобы упростить задачу для SO. Возможно, я ошибаюсь, но не думаю, что здесь можно использовать алгоритмы сортировки.
Вы можете создать отображение элементов на числа таким образом, что при его применении к одной из последовательностей она будет отсортирована. Затем вы можете применить это сопоставление к другой последовательности. После этого вы можете подсчитать количество свопов, необходимое для пузырьковой сортировки второй последовательности: (площадка)
use std::collections::HashMap;
use core::hash::Hash;
use std::collections::HashSet;
fn create_mapping<T: Eq + Hash>(seq: &[T]) -> HashMap<&T, usize> {
let mut ret = HashMap::new();
for (i, elem) in seq.iter().enumerate() {
ret.insert(elem, i);
}
ret
}
fn dist<T: Eq + Hash>(lhs: &[T], rhs: &[T]) -> Option<usize> {
// Distance function is not defined for differing lengths
if lhs.len() != rhs.len() {
return None;
}
let lhs_c: HashSet<_> = lhs.iter().collect();
let rhs_c: HashSet<_> = rhs.iter().collect();
// Distance function is not defined if an element occurs more than once in each sequence
if lhs.len() != lhs_c.len() || rhs.len() != rhs_c.len() {
return None;
}
// Distance function is not defined if the sequences don't share all elements
if lhs_c != rhs_c {
return None;
}
// Calculate the edit distance of `lhs` and `rhs` with only adjacent transpositions.
let mapping = create_mapping(lhs);
let rhs_mapped: Vec<usize> = rhs.iter().map(|e|mapping[e]).collect();
Some(bubble_sort_length(&rhs_mapped))
}
fn bubble_sort_length(seq: &[usize]) -> usize {
let mut ret = 0;
for i in 0..seq.len() {
for j in (i+1)..seq.len() {
if seq[i]>seq[j] {
ret += 1;
}
}
}
ret
}
#[test]
fn dist_is_none_for_differing_lengths() {
assert_eq!(None, dist(&["b", "a", "c"], &["a", "b", "c", "d"]));
}
#[test]
fn dist_is_none_with_duplicates() {
assert_eq!(None, dist(&["b", "a", "c", "a"], &["a", "b", "c", "d"]));
assert_eq!(None, dist(&["a", "b", "c", "d"], &["a", "b", "c", "c"]));
}
#[test]
fn dist_is_none_with_non_empty_difference() {
assert_eq!(None, dist(&["a", "b", "c"], &["d", "e", "f"]));
}
#[test]
fn dist_bac_to_abc_is_one() {
assert_eq!(Some(1), dist(&["b", "a", "c"], &["a", "b", "c"]));
}
#[test]
fn dist_bca_to_bac_is_one() {
assert_eq!(Some(1), dist(&["b", "c", "a"], &["b", "a", "c"]));
}
#[test]
fn dist_cab_to_abc_is_two() {
assert_eq!(Some(2), dist(&["c", "a", "b"], &["a", "b", "c"]));
}
#[test]
fn dist_cdab_to_abcd_is_four() {
assert_eq!(Some(4), dist(&["c", "d", "a", "b"], &["a", "b", "c", "d"]));
}
Этот алгоритм будет иметь временную сложность O(n²)
, где n
— длина последовательности. Вероятно, есть лучший способ реализовать функцию bubble_sort_length
(см. https://www.geeksforgeeks.org/inversion-count-in-array-using-merge-sort/, которая утверждает, что имеет временную сложность O(n*log(n))
)
«вы можете предположить, что в последовательности нет дубликатов» → вы имеете в виду, что каждый символ будет появляться не более одного раза в каждой последовательности?