Задача: В имеющемся словаре найти группы слов, записанных одними и теми же буквами и отличающиеся только их порядком, то есть перестановкой, например, (КОРМА, КОМАР).
Код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
#include <iostream> #include <stdio.h> #include <algorithm> #include <string> using namespace std; int count_words (string s) //функция подсчитывает слова в строке { s+=' '; int a = 0; for (size_t i=0; i<s.length(); i++) if (s[i]==' ') a++; return a; } string * cut_words (string s) //функция разрезает строку на слова и возвращает массив { s+=' '; int c = 0; int p = 0; int i=0; string *s_return = new string [count_words(s)]; while (s.length() != 0) { if (s[i] != ' ') { c++; i++; } else { i=0; s_return[p] = s.substr(0, c); s.erase(0, c+1); c = 0; p++; } } return s_return; } int main () { string s; getline (cin, s); string *s_return; s_return = cut_words(s); for (int i=0; i<count_words(s); i++) { string s_tmp = s_return[i]; if (s_tmp.empty()) //если ячейка пуста, то продолжаем дальше { continue; } else //а если нет, то ищем перестановки. { int p = 0; for (int j=i+1; j<count_words(s); j++) { if ( is_permutation (s_tmp.begin(), s_tmp.end(), s_return[j].begin())) //если перестановка if (s_tmp.length() == s_return[j].length()) //и если одинаковая длина слов { cout<<s_return[j]<<endl; //выводим на экран s_return[j].clear(); //очищаем строку p++; } } if (p>=1) cout<<s_tmp<<endl; } } return 0; } |
Тесты:
Алгоритм:
Для решения данной задачи я воспользовался возможностью, которую мне предоставляет библиотека <algorithm> . Поскольку мне надо было проверить, являются ли строки перестановками строки s_tmp, которую я ставлю в роли исходной с каждой итерацией, я воспользовался функцией is_permutation . Если некая строка является перестановкой, то она выводится на экран и стирается. С последующими итерациями будет проводится проверка на наличие на том или ином месте строки. Если строки обнаружено не будет, программа перейдёт к следующей итерации.
Для проверки правильности работы программы, воспользуйтесь ссылкой .
UPD: Поскольку я понял, что вводить вначале количество слов неудобно, я решил написать функцию, которая сама создаст массив из введённой мной строкой.
— Посмотрите, пожалуйста, здесь почти в конце перед видеолекциями должен быть словарь английских слов.
— И сюда нужно заглянуть.
— clear() не очищает память. Длине строки присваивается значение 0, но capacity не изменяется и все символы строки остаются в памяти.
— Пожалуйста, печатайте только те группы слов, где их больше одного. Каждую группу с новой строки.
Исправлено. C-string выложу позже.
Насчёт теста «Все слова»: работало долго, дымясь, пыхтя и проклиная меня, но выдало ответ.
— Добавьте в метки слово «анаграмма».
— В 62-й строке лучше пробел, чем новая строка. Анаграммы будут в отдельной строке.
— Ещё один небольшой недочёт — если встречаются одинаковые слова их не стоит считать анаграммой.
— Основная проблема. Программа не успевает за 15 секунд отработать словарь из ~2500 слов. Это нереально долго. Для проверки я написал простой код с квадратичной сложностью по числу слов. Он успевает. Используйте его для тестирования если нужно. Но программа должна успеть обработать все данные из тестового словаря.
Выполнить
Существенно ускорить (до n*log(n)) можно при помощи специальной сортировки. Нужно составить алгоритм компаратора, который сравнивает строки по числу использованных букв. Самый простой способ сделать это — отсортировать буквы в словах. После сортировки по отсортированным словам (забавно звучит!), все анаграммы окажутся рядом. Это добавит несколько строк кода, но программа будет существенно быстрее:
Выполнить
Дальнейшее сокращение времени работы программы потребует дополнительной памяти для хранения сортированных побуквенно слов в виде ключей к обычным словам (т.е. структур вида key=>value).