Вычисление расстояния последовательности с соседними транспозициями без вставки или удаления

Как я могу вычислить числовое расстояние между двумя последовательностями при условии, что элементы последовательности можно менять местами только с соседними элементами? Вставка и удаление не допускаются. Функция расстояния должна определять количество операций обмена, необходимых для извлечения одной строки из другой. Более того, функция расстояния не определена для последовательностей разной длины или если последовательности не используют одни и те же символы. Наконец, вы можете предположить, что в последовательности нет дубликатов.

Основываясь на моих исследованиях, мне понадобится что-то вроде расстояния Дамерау–Левенштейна, но я не знаю, как правильно изменить функцию расстояния.

Пример

Для последовательности 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 22.04.2024 09:39

@Jmb, да, твое понимание правильное.

E Y 22.04.2024 13:04

Это то же самое, что количество свопов, необходимое для пузырьковой сортировки одной из последовательностей в другую. См. stackoverflow.com/questions/20035505/… и geeksforgeeks.org/inversion-count-in-array-using-merge-sort

true equals false 22.04.2024 17:44

@trueequalsfalse, к сожалению, тип T не заказан (не реализует PartialOrd). Я использовал str только в тестах, чтобы упростить задачу для SO. Возможно, я ошибаюсь, но не думаю, что здесь можно использовать алгоритмы сортировки.

E Y 23.04.2024 11:57
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
4
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете создать отображение элементов на числа таким образом, что при его применении к одной из последовательностей она будет отсортирована. Затем вы можете применить это сопоставление к другой последовательности. После этого вы можете подсчитать количество свопов, необходимое для пузырьковой сортировки второй последовательности: (площадка)

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)))

Другие вопросы по теме