Проверьте мой код анаграммы от собеседования в прошлом

Имел следующее как вопрос об интервью только что и дросселировал настолько плохо на базовом синтаксисе, что мне не удалось совершенствоваться (после того как адреналин умирает, кодирование идет из окна.)

Учитывая список строки, возвратите список наборов строк, которые являются анаграммами входного набора. т.е. "собака", "бог", "нечто" должно возвратить {"собаку", "бог"}. Позже, я создал код самостоятельно как проверку работоспособности, и это было вокруг теперь некоторое время. Я приветствовал бы вход на нем, чтобы видеть, пропустил ли я что-нибудь или если я, возможно, сделал это намного более эффективно. Возьмите его в качестве шанса улучшить меня и изучить другие методы:


void Anagram::doWork(list input, list> &output)
{
  typedef list < pair < string, string>> SortType;
  SortType sortedInput;
// sort each string and pair it with the original for(list< string >::iterator i = input.begin(); i != input.end(); ++i) { string tempString(*i); std::sort(tempString.begin(), tempString.end()); sortedInput.push_back(make_pair(*i, tempString)); }
// Now step through the new sorted list for(SortType::iterator i = sortedInput.begin(); i != sortedInput.end();) { set< string > newSet;
// Assume (hope) we have a match and pre-add the first. newSet.insert(i->first);
// Set the secondary iterator one past the outside to prevent // matching the original SortType::iterator j = i; ++j;
while(j != sortedInput.end()) { if(i->second == j->second) { // If the string matches, add it to the set and remove it // so that future searches need not worry about it newSet.insert(j->first); j = sortedInput.erase(j); } else { // else, next element ++j; } }
// If size is bigger than our original push, we have a match // - save it to the output if(newSet.size() > 1) { output.push_back(newSet); }
// erase this element and update the iterator i = sortedInput.erase(i); } }

Вот вторая передача в этом после рассмотрения комментариев и изучения немного больше:


void doBetterWork(list input, list> &output)
{
  typedef std::multimap< string, string > SortedInputType;
  SortedInputType sortedInput;
  vector< string > sortedNames;
for(vector< string >::iterator i = input.begin(); i != input.end(); ++i) { string tempString(*i); std::sort(tempString.begin(), tempString.end()); sortedInput.insert(make_pair(tempString, *i)); sortedNames.push_back(tempString); }
for(list< string >::iterator i = sortedNames.begin(); i != sortedNames.end(); ++i) { pair< SortedInputType::iterator,SortedInputType::iterator > bounds; bounds = sortedInput.equal_range(*i);
set< string > newSet; for(SortedInputType::iterator j = bounds.first; j != bounds.second; ++j) { newSet.insert(j->second); }
if(newSet.size() > 1) { output.push_back(newSet); }
sortedInput.erase(bounds.first, bounds.second); } }

6
задан Michael Dorgan 25 April 2010 в 18:31
поделиться

2 ответа

Лучший способ сгруппировать анаграммы - отобразить строки в какое-то представление гистограммы.

 FUNCTION histogram
 [input] -> [output]

 "dog"   -> (1xd, 1xg, 1xo)
 "god"   -> (1xd, 1xg, 1xo)
 "foo"   -> (1xf, 2xo)

По сути, с помощью линейного сканирования строки вы можете создать гистограмму, показывающую, сколько букв в ней содержится. Небольшой конечный алфавит делает это еще проще (например, с A – Z у вас просто есть массив из 26 чисел, по одному на каждую букву).

Итак, анаграммы - это просто слова с одинаковой гистограммой.

Тогда у вас может быть структура данных с несколькими картами, которая отображает гистограмму на список слов, которые имеют эту гистограмму.

MULTIMAP
[key]           => [set of values]

(1xd, 1xg, 1xo) => { "dog", "god" }
(1xf, 2xo)      => { "foo" }

Уловка канонической формы

Вместо работы с гистограммами вы также можете работать с «канонической формой» строк. По сути, вы определяете для каждой строки, какова ее каноническая форма, и два слова являются анаграммами, если они имеют одинаковую каноническую форму.

Одной из удобных канонических форм является сортировка букв в строке.

FUNCTION canonize
[input]  -> [output]

"dog"    -> "dgo"
"god"    -> "dgo"
"abracadabra" -> "aaaaabbcdrr"

Обратите внимание, что это всего лишь один шаг после подхода гистограммы: вы, по сути, выполняете сортировку с подсчетом для сортировки букв.

Это наиболее практичное решение вашей проблемы на реальном языке программирования.

Сложность

Создание гистограммы / канонической формы слова составляет практически O (1) (конечный размер алфавита, конечная максимальная длина слова).

При хорошей реализации хеширования получить и поместить на мультиотображении равно O (1) .

Вы даже можете иметь несколько мультиотображений, по одному на каждое слово.

Если есть N слов, то размещение всех слов в мультиотображениях будет O (N) ; тогда вывод каждой группы анаграмм просто сбрасывает значения в мультиотображениях. Это тоже можно сделать в O (N) .

Это, безусловно, лучше, чем проверка, является ли каждая пара слов анаграммой (алгоритм O (N ^ 2) ).

14
ответ дан 8 December 2019 в 14:41
поделиться

Я буду рассматривать это как бесплатную функцию get_anagrams , поскольку класс Anagram не работает что-нибудь еще.

Выбор списка и набора ничуть не лучше, чем что-либо еще, поэтому пока я буду делать их оба векторными . Фактически, на выходе может быть только одна плоская последовательность. Назовите его anagram_sort . Я возьму спецификацию «список» и «набор» в качестве базовых конструкций, а не буквальных шаблонов классов. Также «данный… возврат» можно в общих чертах интерпретировать как изменение ввода на месте. При желании вызывающий может создать копию.

struct anagram_less { bool operator()( string const &l, string const &r ) {
    string sl( l ), sr( r );
    sort( sl.begin(), sl.end() );
    sort( sr.begin(), sr.end() );
    return sl < sr;
} };

void anagram_sort( vector< string > &v ) { // "the answer"
    sort( v.begin(), v.end(), anagram_less );
} // 10 lines

   // usage:

void print_anagrams( vector< string > &&v ) { // destructive => use rvalue ref
    anagram_sort( v );

    for ( vector< string >::iterator i = v.begin(); i != v.end(); /*++i*/ ) {

        vector< string >::iterator e // find end of run of anagrams
            = adjacent_find( i, v.end(), anagram_less() );
        if ( e != v.end() ) ++ e; // usually need this after adjacent_find :v(

        cout << "( ";
        for ( ; i != e; ++ i ) {
            cout << * i << " ";
        }
        cout << ")" << endl;
    }
}

Это неоптимально, так как слова сортируются повторно. Я предпочитаю сократить количество вопросов в интервью, чем делать что-то быстрое «на бумаге».

Чтобы выжать последний бит производительности, я бы, вероятно, сделал копию ввода с прикрепленными индексами серийных номеров, отсортировал символы строк, отсортировал строки, а затем использовал индексы для изменения порядка входного вектора. используя этот алгоритм . Конечное время выполнения с низким коэффициентом O (n log n), как и должно быть.

1
ответ дан 8 December 2019 в 14:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: