Поиск часто встречающихся элементов в массиве
Время на прочтение
5 мин
Количество просмотров 114K
Задача: в массиве длиной N найти элемент, который повторяется больше N/2 раз.
Казалось бы, чего тут думать? Возьмём Dictionary<значение элемента, число появлений>, за один проход по массиву сосчитаем появления каждого элемента, потом выберем из словаря искомый элемент. Решение за O(N), куда может быть ещё быстрее?
Есть один нюанс: для словаря нам потребуется O(N) дополнительной памяти — в несколько раз больше размера исходного массива, и это при реализации словаря хоть хэш-таблицей, хоть деревом. Что будем делать, если наша цель — обработка сигнала неким устройством с маленькой памятью? Массив — замеры уровня сигнала, из которых один — «настоящий» передаваемый уровень, а остальные — шум и помехи. Неужели придётся для определения «настоящего» уровня возиться с хэш-таблицами и деревьями?
К счастью, нет: достаточно O(1) дополнительной памяти, и по-прежнему одного прохода по массиву. Алгоритм Бойера-Мура — тех самых Бойера и Мура, что придумали намного более известный алгоритм поиска подстроки — проще всего представить следующим образом: на вечеринке собрались N людей, и на каждом по одному элементу из массива. Когда встречаются двое, у которых элементы разные, они присаживаются это обсудить. В конце концов останутся стоять только люди с одинаковыми элементами; очевидно, это тот самый элемент, который встречался больше N/2 раз.
Реализация алгоритма Бойера-Мура занимает всего несколько строк:
int* majority(int[] array, int N) {
int confidence = 0; // количество людей, не нашедших пары и оставшихся стоять
int* candidate = NULL; // последний человек, не нашедший пару --
// возможно, его элемент встречается чаще всего
// проходим по массиву и усаживаем пары с разными элементами
for (int i = 0; i<N; i++) {
// если до сих пор все сидят, то следующему пока придётся постоять
if (confidence == 0) {
candidate = array+i;
confidence++;
}
// иначе следующий либо садится с одним из стоящих,
// либо будет стоять вместе с ними, если у него такой же элемент
else if (*candidate == array[i]))
confidence++;
else
confidence--;
}
return confidence > 0 ? candidate : NULL;
}
В конце мы получаем «наиболее вероятного кандидата» на присутствие в N/2 экземплярах: если такой элемент в массиве действительно существует, то он будет найден; если же такого элемента нет, то возможно, стоять останется просто какой-то бедолага, которому не хватило пары. Для более строгой реализации majority
требуется пройти по массиву второй раз и проверить, действительно ли найденный элемент встречается требуемое количество раз.
Усложним задачу. Теперь в массиве длиной N надо найти элементы, которые повторяются больше N/3 раз — если есть два, то оба, если есть один, то один.
Например, нашему устройству с маленькой памятью нужно принять двоичный сигнал с неизвестными уровнями нуля и единицы, но известно, что примерно половину времени передаётся ноль, примерно половину времени — единица, а любые отличные от них уровни сигнала — это помехи и дребезг от переключения.
Идею прошлого алгоритма несложно обобщить и для троек: пусть люди с разными элементами рассаживаются по трое. Значит, в конце вечеринки останутся стоять люди максимум с двумя разными элементами. Если какой-то элемент встречался больше N/3 раз, значит человек с ним гарантированно останется стоять, ведь сидящих троек получится не больше N/3. Как и в прошлом случае, если искомые элементы существуют — то они будут найдены, но если их нет — то найтись может кто попало.
Код мало отличается от предыдущего: по-прежнему один проход по массиву и O(1) дополнительной памяти.
void majority(int[] array, int N, int** cand1, int** cand2) {
int conf1 = 0, conf2 = 0; // количество стоящих людей с двумя элементами
*cand1 = NULL; *cand2 = NULL; // два стоящих человека с разными элементами
// проходим по массиву и усаживаем тройки с разными элементами
for (int i = 0; i<N; i++) {
// у следующего такой же элемент, как у стоящих? тогда встанет вместе с ними
if (conf1 > 0 && **cand1 == array[i])
conf1++;
else if (conf2 > 0 && **cand2 == array[i])
conf2++;
else // может, стоят только с одним элементом, или вообще стоящих нет?
if (conf1 == 0) {
*cand1 = array+i;
conf1++;
} else if (conf2 == 0) {
*cand2 = array+i;
conf2++;
}
else { // стоят с двумя другими элементами, так что усаживаем всю тройку
conf1--;
conf2--;
}
}
if(conf1 == 0) *cand1 = NULL;
if(conf2 == 0) *cand2 = NULL;
}
Этот алгоритм опубликован в 1982 американскими учёными Джаядевом Мисрой и Дэвидом Грисом (Jayadev Misra & David Gries), и в их работе используется более скучная метафора — мешок с N числами, из которого они извлекают по 3 разных числа за каждую операцию. Кроме того, их псевдокод не похож ни на один понятный современному программисту язык. Мне больше понравилось объяснение их алгоритма в позапрошлогоднем конспекте лекций американского профессора Амита Чакрабарти.
В наиболее общей форме, когда в массиве длиной N надо найти элементы, которые повторяются больше N/k раз — придётся-таки воспользоваться словарём. Хранить в словаре мы будем только те элементы, с которыми люди стоят — т.е. не больше k-1 записей.
int[] majority(int[] array, int N, int k) {
// словарь стоящих людей изначально пуст
Dictionary<int,uint> candidates = new Dictionary<int,uint>{};
// проходим по массиву и усаживаем группы по k
for (int i = 0; i<N; i++) {
// у следующего такой же элемент, как у стоящих? тогда встанет вместе с ними
if (candidates.ContainsKey(array[i]))
candidates[array[i]]++;
else // может, стоят менее чем с k-1 элементами?
if (candidates.Count < k - 1)
candidates[array[i]] = 1;
else // стоят с k-1 другими элементами, так что усаживаем всю группу
foreach(int l in candidates.Keys)
if (candidates[l]-- == 0) // (**)
candidates.Remove(l); // (*)
}
return candidates.Keys.ToArray();
}
В этой наиболее общей форме алгоритма — по-прежнему один проход по массиву и O(k) дополнительной памяти. Если мы пользуемся для реализации словаря хэш-таблицей, а все записи в словаре свяжем в список — тогда общая сложность алгоритма останется линейной: строчка (*) с удалением записи из словаря выполняется самое большее N раз, ведь на каждой итерации основного цикла в словарь добавляется не больше одной записи. Читателям в качестве упражнения предлагается понять, почему строчка (**) не нарушает линейности алгоритма.
Таким образом наше устройство с маленькой памятью смогло бы общаться с одним пушистым зверьком, недавно препарированным хабраумельцами. Сигналы этого зверька имеют пять логических уровней: полагаем k=6, и получаем все пять уровней прямо на ходу, даже без сохранения сигнала в память. Нужно только обеспечить протоколом, чтобы все пять уровней встречались в сигнале одинаково часто.
Для алгоритма Мисры-Гриса упоминаются и другие применения. Например, можно следить в реальном времени за трафиком в сети, и если некий один хост потребляет непропорционально большую часть трафика — начинать расследование. Так же можно следить за кликами по баннерам, за финансовыми транзакциями, за потоком инструкций в моделируемом процессоре… В общем, всюду, где большое число повторений — подозрительная аномалия.
За оживление текста иллюстрациями надо благодарить Nitatunarabe
В массиве надо найти самое(самые) часто повторяющееся(повторяющиеся) числа.
-
При этом нужно вывести всех их, если есть одинаковое число повторяющихся: массив «1 8 3 4 4 1», то должно быть выведено: 1 1 4 4.
-
А если только одно значение повторяется много раз, то из них должно быть выведено только одно. массив «2, 2, 2, 5, 7», ответ: 2.
-
Если повторяющихся совсем нет то ничего не должно вывестись.
У меня только 2 пункт не выполняется, как можно исправить? Код на с++.
int main()
{
int max{}, postmax{}, index{}; const int n = 5; bool flag = false;
int mas[n] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (i == j) continue;
if (mas[i] == mas[j])
{
postmax++;
}
}
if (postmax > max) {
max = postmax;
index = i;
flag = true;
}
else {
if (postmax == max && max != 0) {
cout << mas[index] << endl;
index = i;
}
}
postmax = 0;
}
if (flag) cout << mas[index] << endl;
return 0;
}
задан 29 окт 2021 в 15:33
AnvarAnvar
14 бронзовых знака
4
раз можно использовать c++ (и стандартные библиотеки соответственно), то я бы делал в лоб
-
использовал бы словарь
std::map<int, int>
-
прошелся бы по всем числам в массиве и если числа нет в словаре, добавлял бы
(число, 1)
иначе увеличивал бы значение, соответствующее числу
после этого в словаре содержатся числа и сколько раз они встречаются
-
дальше прошел бы по всем ключам слова и определил бы 1 из 3х состояний — вообще нет повторений (все значения равны 1), есть одно повторение (только одно значение больше 1), есть несколько повторений
-
на основании шага 3) вывел бы соответствующий результат
ответ дан 29 окт 2021 в 15:54
ZhiharZhihar
36.9k4 золотых знака25 серебряных знаков67 бронзовых знаков
11
What would be the best approach to create the function getMostFrequentlyOccurringItem()
below?
//should return "paragraph"
echo getMostFrequentlyOccurringItem(array('line', 'paragraph', 'paragraph'));
//should return "line"
echo getMostFrequentlyOccurringItem(array('wholeNumber', 'line', 'line', 'line'));
//should return null
echo getMostFrequentlyOccurringItem(array('wholeNumber', 'wholeNumber', 'paragraph', 'paragraph'));
//should return "wholeNumber"
echo getMostFrequentlyOccurringItem(array('wholeNumber', '', '', ''));
function getMostFrequentlyOccurringItem($items) {
//...
}
Answer:
Thanks Adam, here’s my finished solution:
http://tanguay.info/web/index.php?pg=codeExamples&id=396
asked Oct 28, 2010 at 16:11
Edward TanguayEdward Tanguay
188k313 gold badges711 silver badges1043 bronze badges
Start with array_count_values()
, and massage the output to your liking.
php> =array_count_values(array('wholeNumber', 'line', 'line', 'line'))
array(
"wholeNumber" => 1,
"line" => 3,
)
arsort($counts, SORT_NUMERIC)
will sort the array_count_values()
output by most frequent first.
answered Oct 28, 2010 at 16:14
Annika BackstromAnnika Backstrom
13.9k6 gold badges45 silver badges52 bronze badges
3
This is a candidate situation where you can apply the linear-time majority vote algorithm.
The link goes into a great explanation on how to apply this brilliantly simple algorithm. Note that if you indeed want to return null to indicate a tie, this will require two passes over the data instead of one.
answered Oct 28, 2010 at 16:21
Mark RushakoffMark Rushakoff
248k45 gold badges405 silver badges397 bronze badges
2
How about implementing an associative array of key and counter.
answered Oct 28, 2010 at 16:12
Implode the array into a string separated by comma or space. Then use preg_match (http://au2.php.net/manual/en/function.preg-match.php) to find the number of occurrences of a specified string. The pattern can be customized to exact match or occurrence match!
answered Oct 28, 2010 at 20:07
zerodinzerodin
8575 silver badges9 bronze badges
Учитывая отсортированный массив, содержащий дубликаты, эффективно найдите частоту каждого элемента, не просматривая весь массив.
Например,
Input: [2, 2, 2, 4, 4, 4, 5, 5, 6, 8, 8, 9]
Output: {2: 3, 4: 3, 5: 2, 6: 1, 8: 2, 9: 1}
Explanation:
2 and 4 occurs thrice
5 and 8 occurs twice
6 and 9 occurs once
Потренируйтесь в этой проблеме
Простым решением было бы запустить линейный поиск в массиве и подсчитать количество вхождений каждого элемента и, наконец, распечатать их. Проблема с этим подходом заключается в том, что его временная сложность в наихудшем случае равна O(n), куда n
— входной размер, и мы сканируем весь массив (нарушая ограничения задачи).
Мы можем легко решить эту проблему в O(m.log(n)) время, используя тот факт, что ввод отсортирован и содержит дубликаты. Здесь m
— общее количество различных элементов в массиве, а n
размер ввода.
1. Рекурсивная реализация
Идея состоит в том, чтобы разделить массив на две половины и с помощью повторяться для обеих половинок. Базовое условие проверяет, совпадает ли последний элемент подмассива с его первым элементом. Если оба равны, то это означает, что все элементы подмассива похожи (поскольку массив отсортирован), и мы обновляем количество элементов на общее количество элементов в подмассиве (за постоянное время).
Алгоритм может быть реализован следующим образом на C++, Java и Python:
C++
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 |
#include <iostream> #include <unordered_map> using namespace std; // Функция для нахождения частоты каждого элемента в отсортированном массиве void findFrequency(int nums[], int n, unordered_map<int, int> &freq) { // если все элементы в подмассиве nums[0…n-1] равны, // затем увеличиваем количество элементов на `n` if (nums[0] == nums[n — 1]) { freq[nums[0]] += n; return; } // делим массив на левый и правый подмассивы и повторяем findFrequency(nums, n/2, freq); findFrequency(nums + n/2, n — n/2, freq); } int main() { int nums[] = { 2, 2, 2, 4, 4, 4, 5, 5, 6, 8, 8, 9 }; int n = sizeof(nums) / sizeof(int); // найти частоту каждого элемента массива и сохранить его в карте unordered_map<int, int> freq; findFrequency(nums, n, freq); // вывести частоту for (auto pair: freq) { cout << pair.first << » occurs « << pair.second << » timesn»; } return 0; } |
Скачать Выполнить код
результат:
9 occurs 1 times
8 occurs 2 times
2 occurs 3 times
4 occurs 3 times
5 occurs 2 times
6 occurs 1 times
Java
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 |
import java.util.HashMap; import java.util.Map; class Main { // Функция для нахождения частоты каждого элемента в отсортированном массиве public static void findFrequency(int[] nums, int left, int right, Map<Integer, Integer> freq) { if (left > right) { return; } // если все элементы подмассива nums[left…right] равны, // затем увеличиваем количество элементов на `n` if (nums[left] == nums[right]) { Integer count = freq.get(nums[left]); if (count == null) { count = 0; } freq.put(nums[left], count + (right — left + 1)); return; } int mid = (left + right)/2; // делим массив на левый и правый подмассивы и повторяем findFrequency(nums, left, mid, freq); findFrequency(nums, mid + 1, right, freq); } public static void main(String[] args) { int[] nums = { 2, 2, 2, 4, 4, 4, 5, 5, 6, 8, 8, 9 }; // найти частоту каждого элемента массива и сохранить его в карте Map<Integer, Integer> freq = new HashMap<>(); findFrequency(nums, 0, nums.length — 1, freq); System.out.println(freq); } } |
Скачать Выполнить код
результат:
{2=3, 4=3, 5=2, 6=1, 8=2, 9=1}
Python
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 |
# Функция для нахождения частоты каждого элемента в отсортированном списке def findFrequency(nums, left, right, freq): if left > right: return #, если все элементы в подсписке nums[left…right] равны, # затем увеличивает количество элементов на `n` if nums[left] == nums[right]: count = freq.get(nums[left]) if count is None: count = 0 freq[nums[left]] = count + (right — left + 1) return mid = (left + right) // 2 # разделить список на левый и правый подсписки и повторить findFrequency(nums, left, mid, freq) findFrequency(nums, mid + 1, right, freq) if __name__ == ‘__main__’: nums = [2, 2, 2, 4, 4, 4, 5, 5, 6, 8, 8, 9] # найти частоту каждого элемента в списке и сохранить его в словаре freq = {} findFrequency(nums, 0, len(nums) — 1, freq) print(freq) |
Скачать Выполнить код
результат:
{2: 3, 4: 3, 5: 2, 6: 1, 8: 2, 9: 1}
2. Итеративная реализация
Ниже приведена итеративная реализация алгоритма на C++, Java и Python. Реализация обновляет частоту каждого отдельного элемента массива по одному и динамически увеличивает/уменьшает коллекцию, чтобы учитывать каждый элемент.
C++
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 |
#include <iostream> #include <vector> #include <unordered_map> using namespace std; // Функция для нахождения частоты каждого элемента в отсортированном массиве unordered_map<int, int> findFrequency(vector<int> const &nums) { // найти частоту каждого элемента массива и сохранить его в карте unordered_map<int, int> freq; int n = nums.size(); // область поиска nums[low…high] int low = 0, high = n — 1, mid; // цикл до тех пор, пока пространство поиска не будет исчерпано while (low <= high) { // если nums[low…high] состоит только из одного элемента, обновить его счетчик if (nums[low] == nums[high]) { freq[nums[low]] += high — low + 1; // теперь отбрасываем nums[low…high] и продолжаем поиск // в nums[high+1… n-1] для nums[low] low = high + 1; high = n — 1; } else { // уменьшаем область поиска high = (low + high) / 2; } } return freq; } int main() { vector<int> nums = { 2, 2, 2, 4, 4, 4, 5, 5, 6, 8, 8, 9 }; unordered_map<int, int> freq = findFrequency(nums); for (auto pair: freq) { cout << pair.first << » occurs « << pair.second << » timesn»; } return 0; } |
Скачать Выполнить код
результат:
Java
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 |
import java.util.HashMap; import java.util.Map; class Main { // Функция для нахождения частоты каждого элемента в отсортированном массиве public static Map<Integer, Integer> findFrequency(int[] nums) { // Карта для хранения частоты каждого элемента массива Map<Integer, Integer> freq = new HashMap<>(); // область поиска nums[left…right] int left = 0, right = nums.length — 1; // цикл до тех пор, пока пространство поиска не будет исчерпано while (left <= right) { // если nums[left…right] состоит только из одного элемента, обновить его счетчик if (nums[left] == nums[right]) { freq.put(nums[left], freq.getOrDefault(nums[left], 0) + (right — left + 1)); // теперь отбрасываем nums[left…right] и продолжаем поиск в // nums[right+1… n-1] для nums[left] left = right + 1; right = nums.length — 1; } else { // уменьшаем область поиска right = (left + right) / 2; } } return freq; } public static void main(String[] args) { int[] nums = { 2, 2, 2, 4, 4, 4, 5, 5, 6, 8, 8, 9}; Map<Integer, Integer> freq = findFrequency(nums); System.out.println(freq); } } |
Скачать Выполнить код
результат:
{2=3, 4=3, 5=2, 6=1, 8=2, 9=1}
Python
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 |
# Функция для нахождения частоты каждого элемента в отсортированном списке def findFrequency(nums): # Словарь # для хранения частоты каждого элемента в списке freq = {} # пространство поиска nums[left…right] (left, right) = (0, len(nums) — 1) # Цикл # до тех пор, пока пространство поиска не будет исчерпано while left <= right: #, если nums[left…right] состоит только из одного элемента, обновить его счетчик if nums[left] == nums[right]: freq[nums[left]] = freq.get(nums[left], 0) + (right — left + 1) # теперь отбрасывает nums[left…right] и продолжает поиск в # nums[right+1… n-1] для nums[left] left = right + 1 right = len(nums) — 1 else: # уменьшить пространство поиска right = (left + right) // 2 return freq if __name__ == ‘__main__’: nums = [2, 2, 2, 4, 4, 4, 5, 5, 6, 8, 8, 9] print(findFrequency(nums)) |
Скачать Выполнить код
результат:
{2: 3, 4: 3, 5: 2, 6: 1, 8: 2, 9: 1}
3. Реализация STL
Мы можем легко решить эту проблему, используя также STL. Идея состоит в том, чтобы найти индекс первого и последнего появления каждого отдельного числа в массиве и обновить разницу между двумя индексами на карте. В следующем решении мы будем использовать STL std::upper_bound а также std::lower_bound чтобы найти первое и последнее вхождение каждого элемента, используя алгоритм бинарного поиска.
C++
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 |
#include <iostream> #include <unordered_map> #include <vector> #include <algorithm> using namespace std; // Функция для нахождения частоты каждого элемента в отсортированном массиве unordered_map<int, int> findFrequency(vector<int> const &nums) { // карта для хранения частоты каждого элемента массива unordered_map<int, int> freq; // делаем для каждого отдельного элемента массива auto it = nums.begin(); while (it != nums.end()) { int val = *it; // найти первое вхождение auto low = lower_bound(nums.begin(), nums.end(), val); // найти последнее вхождение auto high = upper_bound(nums.begin(), nums.end(), val); // обновляем разницу в карте freq[val] = high — low; // важно перейти к следующему элементу вектора // (не ближайший следующий) it = it + freq[val]; } // возвращаем карту return freq; } int main() { vector<int> nums { 2, 2, 2, 4, 4, 4, 5, 5, 6, 8, 8, 9 }; unordered_map<int, int> freq = findFrequency(nums); for (auto pair: freq) { cout << pair.first << » occurs « << pair.second << » timesn»; } return 0; } |
Скачать Выполнить код
результат:
9 occurs 1 times
8 occurs 2 times
2 occurs 3 times
4 occurs 3 times
5 occurs 2 times
6 occurs 1 times
69 / 68 / 52 Регистрация: 28.10.2015 Сообщений: 388 |
|
1 |
|
Найти элемент который чаще всего повторяется в массиве29.10.2015, 19:55. Показов 8716. Ответов 6
как найти элемент который чаще всего повторяется в массиве(1)
0 |
Programming Эксперт 94731 / 64177 / 26122 Регистрация: 12.04.2006 Сообщений: 116,782 |
29.10.2015, 19:55 |
6 |
Sigin 225 / 224 / 112 Регистрация: 20.10.2013 Сообщений: 808 |
||||
29.10.2015, 20:22 |
2 |
|||
Самый простой способ — сгруппировать элементы массива:
0 |
69 / 68 / 52 Регистрация: 28.10.2015 Сообщений: 388 |
|
29.10.2015, 20:33 [ТС] |
3 |
Мне нада через for. Можеш помочь найти ошыбку?
0 |
Sigin 225 / 224 / 112 Регистрация: 20.10.2013 Сообщений: 808 |
||||
29.10.2015, 21:05 |
4 |
|||
Сообщение было отмечено Памирыч как решение РешениеНе по теме: Да вот же она – «ошыбку»
Так легче?
0 |
69 / 68 / 52 Регистрация: 28.10.2015 Сообщений: 388 |
|
29.10.2015, 21:22 [ТС] |
5 |
а как в даном случае рандомом заполнить?
0 |
Sigin 225 / 224 / 112 Регистрация: 20.10.2013 Сообщений: 808 |
||||
29.10.2015, 21:29 |
6 |
|||
Сообщение было отмечено Памирыч как решение Решение
0 |
69 / 68 / 52 Регистрация: 28.10.2015 Сообщений: 388 |
|
29.10.2015, 21:31 [ТС] |
7 |
Спасибо
0 |
IT_Exp Эксперт 87844 / 49110 / 22898 Регистрация: 17.06.2006 Сообщений: 92,604 |
29.10.2015, 21:31 |
Помогаю со студенческими работами здесь В заданной строке найти символ, который повторяется чаще всего Найти число в двумерном массиве, которое чаще всего повторяется Найти элемент массива, который чаще всего встречается Найти в одномерном числовом массиве элемент, который наибольшее количество раз повторяется в массиве Массив: Найти в одномерном массиве элемент, который наибольшее количество раз повторяется в массиве Найти согласную, которая повторяется в тексте чаще всего Искать еще темы с ответами Или воспользуйтесь поиском по форуму: 7 |