Время на прочтение
5 мин
Количество просмотров 62K
Описание общей потребности в поиске данных и объектов в базе данных
Поиск данных, а также хранимых процедур, таблиц и других объектов в базе данных является достаточно актуальным вопросом в том числе и для C#-разработчиков, а также и для .NET-разработки в целом.
Достаточно часто может возникнуть ситуация, при которой нужно найти:
- объект базы данных (таблицу, представление, хранимую процедуру, функцию и т д)
- данные (значение и в какой таблице располагается)
- фрагмент кода в определениях объектов базы данных
Существует множество готовых решений как платных, так и бесплатных.
Сначала рассмотрим как можно осуществлять поиск данных и объектов в базе данных с помощью встроенных средств самой СУБД, а затем рассмотрим как это сделать с помощью бесплатной утилиты dbForge Search.
Поиск с помощью встроенных средств самой СУБД
Определить есть ли таблица Employee в базе данных можно с помощью следующего скрипта:
Поиск таблицы по имени
select [object_id], [schema_id],
schema_name([schema_id]) as [schema_name],
[name],
[type],
[type_desc],
[create_date],
[modify_date]
from sys.all_objects
where [name]='Employee';
Результат может быть примерно такой:
Здесь выводятся:
- идентификаторы объекта и схемы, где располагается объект
- название этой схемы и название этого объекта
- тип объекта и описание этого типа объекта
- даты и время создания и последней модификации объекта
Чтобы найти все вхождения строки “Project”, то можно использовать следующий скрипт:
Поиск всех объектов по подстроке в имени
select [object_id], [schema_id],
schema_name([schema_id]) as [schema_name],
[name],
[type],
[type_desc],
[create_date],
[modify_date]
from sys.all_objects
where [name] like '%Project%';
Результат может быть примерно такой:
Как видно из результата, здесь подстроку “Project” содержат не только две таблицы Project и ProjectSkill, но и также некоторые первичные и внешние ключи.
Чтобы понять кому именно принадлежат данные ключи, добавим в вывод поле parent_object_id и его имя и схему, в которой он располагается следующим образом:
Поиск всех объектов по подстроке в имени с выводом родительских объектов
select ao.[object_id], ao.[schema_id],
schema_name(ao.[schema_id]) as [schema_name],
ao.parent_object_id,
p.[schema_id] as [parent_schema_id],
schema_name(p.[schema_id]) as [parent_schema_name],
p.[name] as [parent_name],
ao.[name],
ao.[type],
ao.[type_desc],
ao.[create_date],
ao.[modify_date]
from sys.all_objects as ao
left outer join sys.all_objects as p on ao.[parent_object_id]=p.[object_id]
where ao.[name] like '%Project%';
Результатом будет вывод таблицы с детальной информацией о родительских объектах, т е где определены первичные и внешние ключи:
В запросах используются следующие системные объекты:
- таблица sys.all_objects
- скалярная функция schema_name
Итак, разобрали как найти объекты в базе данных с помощью встроенных средств самой СУБД.
Теперь покажем как найти данные в базе данных на примере поиска строк.
Чтобы найти строковое значение по всем таблицам базы данных, можно воспользоваться следующим решением. Упростим данное решение и покажем как можно найти например значение “Ramiro” с помощью следующего скрипта:
Поиск строковых значений по подстроке во всех таблицах базы данных
set nocount on
declare @name varchar(128), @substr nvarchar(4000), @column varchar(128)
set @substr = '%Ramiro%'
declare @sql nvarchar(max);
create table #rslt
(table_name varchar(128), field_name varchar(128), [value] nvarchar(max))
declare s cursor for select table_name as table_name from information_schema.tables where table_type = 'BASE TABLE' order by table_name
open s
fetch next from s into @name
while @@fetch_status = 0
begin
declare c cursor for
select quotename(column_name) as column_name from information_schema.columns
where data_type in ('text', 'ntext', 'varchar', 'char', 'nvarchar', 'char', 'sysname', 'int', 'tinyint') and table_name = @name
set @name = quotename(@name)
open c
fetch next from c into @column
while @@fetch_status = 0
begin
--print 'Processing table - ' + @name + ', column - ' + @column
set @sql='insert into #rslt select ''' + @name + ''' as Table_name, ''' + @column + ''', cast(' + @column +
' as nvarchar(max)) from' + @name + ' where cast(' + @column + ' as nvarchar(max)) like ''' + @substr + '''';
print @sql;
exec(@sql);
fetch next from c into @column;
end
close c
deallocate c
fetch next from s into @name
end
select table_name as [Table Name], field_name as [Field Name], count(*) as [Found Mathes] from #rslt
group by table_name, field_name
order by table_name, field_name
drop table #rslt
close s
deallocate s
Результат выполнения может быть таким:
Здесь выводятся имена таблиц и в каких столбцах хранится значение, содержащие подстроку “Ramiro”. А также количество найденных входов данной подстроки для найденной пары таблица-колонка.
Чтобы найти объекты, в определениях которых есть заданный фрагмент кода, можно воспользоваться следующими системными представлениями:
- sys.sql_modules
- sys.all_sql_modules
- sys.syscomments
Например, используя последнее представление, можно с помощью следующего скрипта найти все объекты, в определениях которых встречается заданный фрагмент кода:
Поиск фрагмента кода в определениях объектов базы данных
select obj.[object_id],
obj.[name],
obj.[type_desc],
sc.[text]
from sys.syscomments as sc
inner join sys.objects obj on sc.[id]=obj.[object_id]
where sc.[text] like '%code snippet%';
Здесь будет выведен идентификатор, название, описание и полное определение объекта.
Поиск с помощью бесплатной утилиты dbForge Search
Однако, более удобно поиск производить с помощью готовых хороших инструментов. Одним из таких инструментов является dbForge Search.
Для вызова этой утилиты в окне SSMS нажмите на кнопку .
Появится следующее окно поиска:
Обратите внимание на верхнюю панель (слева направо):
- можно переключать режим поиска (ищем DDL (объекты) или данные)
- непосредственно что ищем (какую подстроку)
- учитывать ли регистр, искать точное соответствие слову, искать вхождения:
- группировать результат по типам объектов — кнопка
- выбрать нужные типы объектов для поиска:
- также можно задать несколько баз данных для поиска и выбрать экземпляр MS SQL Server
Это все в режиме поиска объектов, т е когда включен DDL:
В режиме поиска данных изменится только выбор типов объектов:
А именно будут доступны для выбора только таблицы, где и хранятся собственно сами данные:
Теперь как и раньше найдем все вхождения подстроки “Project” в названиях объектов:
Как видно, был выбран режим поиска по DDL-объектам, заполнено что ищем-строка “Project”, остальное все было по умолчанию.
При выделении найденного объекта внизу отображается код определения данного объекта или всего его родительского объекта.
Также можно переместить навигацию на найденный объект, щелкнув на кнопку :
Можно также сгруппировать найденные объекты по их типу:
Обратите внимание, что выводятся даже те таблицы, в которых есть поля, в именах которых содержится подстрока “Project”. Однако, напомним, что режим поиска можно менять: искать полное соответствие/частичное/учитывать регистр или нет.
Теперь найдем значение “Ramiro” по всем таблицам:
Обратите внимание, что внизу отображаются все строки, в которых содержится подстрока “Ramiro” выбранной таблицы Employee.
Также можно переместить навигацию к найденному объекту, нажав как и ранее на кнопку :
Таким образом мы можем искать нужные объекты и данные в базе данных.
Заключение
Были рассмотрены способы поиска как самих данных, так и объектов в базе данных как с помощью встроенных средств самой СУБД MS SQL Server, так и с помощью бесплатной утилиты dbForge Search.
Также от компании Devart есть и ряд других бесплатных готовых решений, полный список которых можно посмотреть здесь.
Источники
- Search_Script.sql
- SSMS
- dbForge Search
- Документация по Microsoft SQL
- Бесплатные решения от компании Devart
Игра Warzone 2 наконец-то вышла и готова занять всё ваше свободное время между работой и сном.
Вы проведете десятки часов, создавая свой арсенал, улучшая снаряжение и обстреливая противников по всей карте в новом режиме DMZ.
Содержание:
Этот режим в стиле Escape from Tarkov в Call of Duty позволяет игрокам, если им удаётся выжить, переносить оружие из игры в игру, наращивая своё снаряжение. Кроме того, DMZ позволяет игрокам путешествовать по открытому миру, наполненному ботами и другими игроками, которые способны забрать всё снаряжение, над которым вы так усердно трудились.
В режиме DMZ в Warzone 2 есть три фракции, и «Тёмный протокол» — одна из них.
Белый лотус, другая фракция, заставит вас собирать информацию о Тёмном протоколе в рамках миссии второго этапа «Охота за жетонами», и эти данные где-то спрятаны.
Чтобы найти разведданные, следует отправиться на север в район Rohan oil. Вам нужно будет пробраться к месту, где они спрятаны. Опасайтесь солдат, патрулирующих местность и ДРУГИХ ИГРОКОВ, это очень популярная локация. Всегда имеет смыл включить вышку БПЛА, если она есть, чтобы понять где находится противник.
Rohan Oil — один из крупнейших работодателей в регионе, нефтеперерабатывающий завод Rohan Oil расположен в стратегическом месте рядом с одними из старейших нефтяных вышек Адала. Обратите внимание на жилые помещения к северу и югу от этого огромного завода.
Фрагменты данных «тёмного протокола» располагаются случайном образом, поэтому имеет смысл проверять все здания и полки в данном районе.
Но есть несколько наиболее частых мест где их можно найти, они обозначены на карте ниже:
На карте обозначены здания, в которых часто встречаются данные Тёмного протокола, но папки с данными могу и просто лежать на полках под открытым небом. Поэтому имеет смысл проверять всё.
Если вы нашли один фрагмент данных, то стоит продолжить поиски, на карте в один момент может находиться сразу несколько. Иногда они лежат рядом.
Как только вы получили данные, вы должны эвакуироваться живым, чтобы завершить миссию.
Операция «Охота за жетонами»
Ещё в этой операции от вас требуется «Собрать и эвакуировать жетоны противника (4).»
Чтобы собрать и эвакуировать 4 вражеских жетона в Warzone 2 DMZ, вам нужно убить четыре настоящих игрока. Жетоны не выпадают из ботов, только из реальных игроков.
Лучший способ сделать это, — взять контракт «Охота на отряд». Вот как выглядит иконка на карте:
Когда вы возьмёте это задание, то для вас на карте будет обозначено расположение вражеского отряда. Это на много всё упрощает. Однако, когда вы попадете в зеленый круг, они получат уведомление, поэтому с этого момента будьте особенно осторожны. Имейте в виду, что вам нужно грабить врагов, которых вы победили, чтобы получить жетоны; вы не получите их автоматически. Каким бы способом вы не получили жетоны, не забудьте эвакуироваться, прежде чем вас самих убьют. В противном случае получение жетона не будет засчитано.
Примечание: вам не нужно собирать все четыре жетона в одном матче. Можно собирать по одному, если хотите.
Что ж… это «обычный» способ получить 4 вражеских жетона. Тем не менее, есть способ сделать это проще.
Простой способ
Вы можете брать жетоны своих напарников! Если играете с друзьями, то можете попросить их пожертвовать собой, чтобы помочь вам выполнить задание в кратчайшие сроки. Вы можете взять жетон, а потом реанимировать своих напарников.
Если играете в отряде со случайными людьми, то рано или поздно один из них погибнет. Тогда перед тем как реанимировать своего коллегу, откройте его рюкзак и возьмите жетон.
Если у вас есть другие идеи или замечания, напишите их в комментариях!
Ghost
«Познакомьтесь с Ghost, самопровозглашенным гуру «Call of Duty». Он провёл так много времени за игрой, что убедил себя, что на самом деле является солдатом. Не верите нам? Просто спросите его о любимых перках и обвесе… а потом убегайте».
Решение
Алгоритм
- Для каждой таблицы конкатенируем текстовые столбцы с некоторым разделителем, которого заведомо не может быть в поисковой строке. Это позволит нам избежать ситуации, когда искомый фрагмент текста может начинаться в одном столбце строки таблицы, а заканчиваться — в другом.
- Для каждой таблицы формируем строку запроса для динамического выполнения.
- Полученный в результате первых двух пунктов запрос будем использовать в качестве источника строк для курсора.
- При обходе курсора будем динамически выполнять построенный оператор. Чтобы избежать вывода всех таблиц, дополним скрипт критерием существования искомых строк в результатах поиска. Тогда на выходе будут только те строки/таблицы, в которых содержится искомая подстрока.
Пункт 1 алгоритма
Столбцы таблиц можно извлечь из системного представления INFORMATION_SCHEMA.COLUMNS. Поскольку у нас появилась замечательная функция CONCAT, то нет особой необходимости выбирать только столбцы строковых типов данных. Дело в том, что функция CONCAT неявно преобразовывает данные к строковому типу и, кроме того, заменяет при этом NULL-значение пустой строкой.
Замечания. Не все типы данных могут быть неявно преобразованы к строке. Устаревший тип IMAGE нельзя преобразовать, поэтому в коде исключается таблица dtproperties, содержащая данные для построения диаграммы и использующая этот тип данных. Помимо этого, имеются типы данных, которые могут быть преобразованы только явно:
- hierarchyid
- sql_variant
- XML
- CLR UDT
Вот скрипт, который выполняет указанные действия:
SELECT TABLE_NAME, STRING_AGG(concat(COLUMN_NAME,',''|'''),',') cols
FROM information_schema.columns
WHERE TABLE_NAME != 'dtproperties'
GROUP BY TABLE_NAME
Ниже приведены результаты выполнения кода. В качестве примера используется учебная база данных сайта.
Пункт 2 алгоритма
Формируем строку запроса, конкатенируя строки. Переменная @search будет содержать поисковую строку. Для примера в качестве поисковой строки используется небезызвестный Bismarck, т.е. мы будем искать текст содержащий слово Bismarck в любом месте строки. Сам поисковый запрос находится во втором столбце результирующего набора, а в первом мы будем выводить имя таблицы. Для нашей задачи имя таблицы в первом столбце не используется в дальнейшем. Но оно может потребоваться для расширения функциональности. Вот к чему мы пришли:
DECLARE @search varchar(200) ='bismarck';
WITH tbls AS
(SELECT TABLE_NAME, STRING_AGG(concat(COLUMN_NAME,',''|'''),',') cols
FROM information_schema.columns
WHERE TABLE_NAME != 'dtproperties'
GROUP BY TABLE_NAME)
SELECT TABLE_NAME, CONCAT('select ''',TABLE_NAME,''' table_name,* from ',
TABLE_NAME,' where concat(',cols,') like ''%', @search,'%''' ) stmt
FROM tbls;
И фрагмент результата:
Напомню, что мы исходим из того, что разделитель столбцов (у нас «|») не должен присутствовать в поисковой фразе. В противном случае, нужно этот символ заменить на другой (другие).
Пункт 3 алгоритма
Описываем и открываем курсор, источником данных для которого будет полученный ранее запрос. Дополнительно опишем две переменных, куда будут заноситься данные, извлекаемые при обходе курсора. Тут все ясно.
DECLARE @t VARCHAR(100);
DECLARE @s VARCHAR(2000);
DECLARE @search VARCHAR(200) ='bismarck';
DECLARE X CURSOR for
WITH tbls AS
(SELECT TABLE_NAME, STRING_AGG(concat(COLUMN_NAME,',''|'''),',') cols
FROM information_schema.columns
WHERE TABLE_NAME != 'dtproperties'
GROUP BY TABLE_NAME)
SELECT TABLE_NAME, CONCAT('select ''',TABLE_NAME,''' table_name,* from ',
TABLE_NAME,' where concat(',cols,') like ''%', @search,'%''' ) stmt
FROM tbls;
OPEN X;
Пункт 4 алгоритма, последний
Обходим курсор, выполняя сформированный оператор во втором столбце курсора. Вернее, не совсем тот. Чтобы убрать неинформативный вывод таблиц, в которых не было найдено совпадение с поисковой фразой, мы дополняем оператор проверкой наличия строк на выходе. В итоге будут получаться операторы такого вида:
if exists(select 'Battles' table_name,* from Battles
where concat(date,'|',name,'|') like '%bismarck%')
select 'Battles' table_name,* from Battles
where concat(date,'|',name,'|') like '%bismarck%';
Возможно, это не очень оптимальный прием, но недостатки компенсируются удобством.
Вот фрагмент кода, выполняющего обход курсора с выполнением поискового оператора:
FETCH X INTO @t,@s;
WHILE @@FETCH_STATUS = 0
begin
SET @s = CONCAT('if exists(',@s,') ',@s,';');
EXECUTE (@s);
FETCH X INTO @t,@s;
END
И, наконец, скрипт целиком:
DECLARE @t VARCHAR(100);
DECLARE @s VARCHAR(2000);
DECLARE @search VARCHAR(200) ='bismarck';
DECLARE X CURSOR for
WITH tbls AS
(SELECT TABLE_NAME, STRING_AGG(concat(COLUMN_NAME,',''|'''),',') cols
FROM information_schema.columns
WHERE TABLE_NAME != 'dtproperties'
GROUP BY TABLE_NAME)
SELECT TABLE_NAME, CONCAT('select ''',TABLE_NAME,''' table_name,* from ',
TABLE_NAME,' where concat(',cols,') like ''%', @search,'%''' ) stmt
FROM tbls;
OPEN X;
FETCH X INTO @t,@s;
WHILE @@FETCH_STATUS = 0
begin
SET @s = CONCAT('if exists(',@s,') ',@s,';');
EXECUTE (@s);
FETCH X INTO @t,@s;
END
CLOSE X;
DEALLOCATE X;
Результаты поиска Бисмарка представлены на рисунке ниже.
Побочным эффектом частого пребывания в Анимусе является «Эффект просачивания», когда пережитые события перемешиваются между собой и провоцируют возникновение галлюцинаций. Видения из прошлого начинают возникать сами по себе и не поддаются контролю. Мозг с нервной системой не справляются с нагрузкой, что приводит со временем к расстройству психики и сводит человека с ума. В подобной ловушке когда-то оказался «Объект 16», который не смог пережить случившееся и покончил с собой, оставив кровавое послание из загадочных глифов будущим узникам Абстерго. Поиском Яблока Эдема и разгадкой Истины Дезмонд неустанно занимался на протяжении всей серии Assassin’s Creed. Перенесенные нагрузки и сильнейший шок в итоге пагубно скажутся на его разуме. В Assassin’s Creed: Revelation его сознание окажется заперто в глубинах Анимуса, где произойдет встреча с «Объект 16», который в доходчивой форме объяснит, что нужно сделать, чтобы пробудиться и вернуться в реальный мир, не став жертвой слияния с предками — Альтаиром и Эцио.
Фрагменты данных Анимуса в Assassin’s Creed: Revelation заменили флаги и перья. Они тесно связаны с пятью воспоминаниями из жизни Дезмонда, которые можно еще раз пережить, войдя в порталы на виртуальном острове. Для открытия порталов нужно найти и собрать во время путешествий по Константинополю и Каппадокии 30 из 100 фрагментов Анимуса. Если при входе в портал игра зависает и вылетает на рабочий стол, необходимо обновить игру патчами до версии 1.01 или выше, где данная ошибка была исправлена. Большинство фрагментов Анимуса находится в зоне досягаемости — у земли или на низкой высоте, оставшиеся доступны только с парашютом (продаются у портных). Во время полета важно контролировать скорость спуска и направление (клавиши [W], [A], [S], [D]). При должной ловкости и сноровке фрагменты Анимуса можно собирать в свободном падении, отцепившись от парашюта клавишей [Shift]. После нахождения первых 50 фрагментов на карте автоматически появляются метки с местоположением оставшихся (все они влияют на 100% прохождение игры).
Фрагменты данных Анимуса в квартале Галаты в Assassin’s Creed: Revelation:
- На стене по пути в западную внутреннюю часть района.
- На стойке в воде возле башни в западной части района.
- На перекладине над аркой в северо-западной части района.
- На перекладине между домами по пути к убежищу ассасинов.
- На платформе с западной стороны Галатской башни. Чтобы добраться до фрагмента Анимуса, нужно прыгать с ближайшего окна по пути к вершине или с крыши башни на парашюте, и в полете благодарить разработчиков за чертову телегу с сеном.
- На дымовой трубе на крыше дома у стены, к юго-западу от Галатской башни.
- На навесе вдоль улицы в центре района, к югу от Галатской башни.
- На дымовой трубе на крыше дома в центре района, к югу от Галатской башни.
- На дымовой трубе на крыше дома в центре района, к юго-востоку от Галатской башни.
- На арке между домами у восточной стены района.
- На перекладине с южной стороны маяка на пристани.
Фрагменты данных Анимуса в северной части квартала Константина в Assassin’s Creed: Revelation:
- На сторожевой вышке в военном лагере, к востоку от мечети Фатих.
- На северном минанарете мечети Фатих.
- В фонтане во дворе мечети Фатих.
- На акведуке напротив входа в мечеть Фатих.
- На вершине сигнальной башни на базе ассасинов.
- На стене рядом с бойцовской площадкой, где был завербован один из рекрутов, к западу от базы ассасинов. Чтобы забраться на перекладину, нужно встать лицом к стене, зажать клавиши [W] + [Пробел] + [ПКМ] и в наивысшей точке нажать клавиши [A] + [Пробел] + [ПКМ], выполнив сложный боковой прыжок, похожий на букву «Г».
- На крыше мечети Фенари-Иса, к юго-западу от базы ассасинов.
- Вдоль троса над площадью между домами, у южных границ района.
- На вершине колонны Марциана в центре района.
- На колонне среди развалин у юго-восточных границ района, к югу от акведука Валента.
Фрагменты данных Анимуса в южной части квартала Константина в Assassin’s Creed: Revelation:
- На западной стене города рядом с мечетью, к северу от главного убежища цыган. Чтобы забраться на платформу, нужно встать лицом к стене, зажать клавиши [W] + [Пробел] + [ПКМ] и в наивысшей точке нажать клавиши [D] + [Пробел] + [ПКМ], выполнив сложный боковой прыжок, похожий на букву «Г».
- На арке мечети у западной стены города, к северу от главного убежища цыган.
- На крыше мечети, к югу от реки Ликус. Чтобы забрать фрагмент Анимуса, нужно спрыгнуть с парашютом с соседнего минарета.
- На тросе между домами в центре района, к северу-востоку от главного убежища цыган.
- На крыше мечети, к северу от базы ассасинов.
- В переулке под навесом между домами, к востоку от базы ассасинов.
- На вершине сигнальной башни на базе ассасинов.
- Во дворике к югу от главного убежища цыган.
- На западной стене города, к югу от главного убежища цыган.
- Среди могил на кладбище, к югу от тоннеля для быстрого перехода.
- На колонне рядом с кладбищенской мечетью, к югу от тоннеля для быстрого перехода.
Фрагменты данных Анимуса в Арсенале в Assassin’s Creed: Revelation:
- У кормы первого корабля.
- На крыше дома, к северо-западу от первого корабля.
- Над аркой западных ворот.
- Во дворе рядом со стеной, к северу от первого корабля.
- На крыше дома, к северу от второго корабля.
- На вершине мачты второго корабля.
- В корпусе недостроенного корябля.
- На платформе перед недостроенным кораблем.
- На платформе у северной стены.
- Под крышей дома у северной стены.
- На перекладине под крышей сарая, к востоку от второго корабля.
Фрагменты данных Анимуса в северной части квартала Баязида в Assassin’s Creed: Revelation:
- На крыше мечети у юго-западных границ района, к западу от тоннеля для быстрого перехода.
- На крыше хозяйственных пристроек с западной стороны мечети Баязида.
- На вершине мечети Баязида.
- На крыше базы воров.
- На колонне за мечетью Зейрек.
- На колонне напротив главного входа в мечеть Баязида.
- В арке акведука Валента. Добраться до фрагмента можно по тросу, проходящему через арку между домами.
- На крыше самого высокого дома, к югу от сигнальной вышки у базы ассасинов.
- На вершине башни в гавани, к северу от тоннеля для быстрого перехода.
- На крыше портового здания в гавани, к северу от тоннеля для быстрого перехода.
- У самой воды рядом с кораблем в гавани, к северу от тоннеля для быстрого перехода.
- На пристройке рядом с мечетью в гавани, к северу от тоннеля для быстрого перехода.
Фрагменты данных Анимуса в южной части квартала Баязида в Assassin’s Creed: Revelation:
- На первой башне рядом с южной гаванью.
- На флагштоке на вершине второй башни рядом с южной гаванью.
- На крыше Маленькой Айи-Софии.
- На крыше прохода к Маленькой Айи-Софии, к востоку от мечети.
- На крыше дома, к востоку от церкви Мирелейон.
- На крыше арки в восточной части района, между церковью Мирелейон и Ипподромом.
- На крыше дома рядом с мечетью в северной части района, к север-востоку от церкви Мирелейон.
- На крыше смотровой вышки, к северу от тоннеля для быстрого перехода.
- На крыше мечети к западу от базы ассасинов.
- На крыше смотровой вышки, к западу от тоннеля для быстрого перехода.
- На одной из колонн Бычьего форума.
Фрагменты данных Анимуса в северной части Имперского квартала в Assassin’s Creed: Revelation:
- Над аркой между пристанью и городом в гавани.
- На перекладине над улицей, к западу от базы ассасинов.
- На перекладите за портовой стеной, к северо-востоку от базы ассасинов.
- На перекладите над торговой лавкой, к востоку от базы ассасинов.
- На перекладине над дорогой к дворцу султана в Топкапи, к востоку от базы ассасинов.
- На перекладине под крышей рынка.
- На перекладине над дорогой, к югу от тоннеля для быстрого перехода рядом с лавкой Софии.
- На перекладине за домом, к северу от тоннеля для быстрого перехода рядом с лавкой Софии.
- На вершине северного минарета мечети Айя-София.
- На крыше беседки в саду мечети Айя-София.
- На балкончике рядом с площадкой, где состоится последняя встреча с принцем Сулейманом, в дворцовом комплексе Топкапи.
Фрагменты данных Анимуса в южной части Имперского квартала в Assassin’s Creed: Revelation:
- На вершине южного минарета мечети Айя-София.
- На канате между домами за мечетью Айя-София.
- На перекладине за домами рядом с форумом Константина.
- На вершине минарета рядом с мечетью, к юго-востоку от форума Константина.
- На перекладине между домами, к юго-востоку от ипподрома.
- На канате между домами рядом со стеной города, недалеко от гавани.
- На крыше между домами, к юго-востоку от базы ассасинов.
- На перекладине над входом в гавань, к югу от базы ассасинов.
- На проходной арке между базой ассасинов и ипподромом.
- На перекладине над дорогой, к западу от ипподрома.
- На перекладине над входом в ипподром, к северо-западу от тоннеля для быстрого перехода.
Вернуться в Каппадокию и собрать оставшиеся фрагменты данных Анимуса можно через раздел «ДНК» в главном меню игры, переиграв любое из поздних воспоминаний в седьмой последовательности. За нахождение 100 фрагментов Анимуса открывается достижение «Предел».
Фрагменты данных Анимуса в Каппадокии в Assassin’s Creed: Revelation:
- Недалеко от пристани в северо-западной части подземного города, где будет убит Мануил Палеолог.
- На платформе над воротами, ведущими к пристани, где будет убит Мануил Палеолог.
- На уступе рядом с дорогой, к северо-востоку от площадки, где будет убит Шах-Кул.
- Рядом с рынком на нижнем уровне подземного города, к северо-востоку от площадки, где будет убит Шах-Кул.
- Над книжной лавкой в юго-западной части подземного города.
- На площадке к югу от центральной глыбы, поддерживающей своды подземного города.
- Под навесом к югу от лавки врача у центральной глыбы, поддерживающей своды подземного города.
- На площадке по пути к тюрьме, где будут держать Диляру, в юго-восточной части подземного города.
- У ступенек перехода за лавкой кузнеца, в восточной части подземного города.
- На площадке недалеко от корабля, за пределами подземного города.
- На платформе под сводами пещеры в северной части подземного города. Чтобы добраться до фрагмента Анимуса, нужно подняться на центральную глыбу, поддерживающую своды подземного города, и добежать по дорожке к лесенке, а с нее уже спрыгнуть на платформу.
- На платформе над лавкой доктора в северной части подземного города.
Битное изображение размером N x M задается совокупностью с 1 и 0, размещенных в соответствующих ячейках. Смежными называются клетки, содержащие общую «сторону» или «угол». Фрагментом называется совокупность всех смежных ячеек с единичками на фоне нулей. Подобными называются фрагменты, в которых равное количество 1 и одинаковое их относительное размещение (подобные также фрагменты, которые повернуты друг относительно друга на угол кратный 90 градусов). В заданном таким образом изображении нужно найти и выделить все разные фрагменты. Под выделением понимается замена всех 1 в определенном виде фрагментов на одну из латинских букв [a-z].
Для выделения подобных фрагментов нужно использовать одну из букв латинского алфавита [a-z]. Для подобных фрагментов — одинаковая буква. Буквы используются в алфавитном порядке. Назначение определенной буквы для фрагмента нужно делать в порядке нахождения новых, просматривая изображение слева направо и по строкам вниз (см. Пример).
Изображение размером 6х3.
http://franko-school.ucoz.com/im.jpg
ограничения:
0 ≤ Ширина изображения ≤ 100
0 ≤ Высота изображения ≤ 100
0 ≤ Всего фрагментов ≤ 500
0 ≤ Различных фрагментов ≤ 26 [a-z]
В каждом из фрагментов не более 160 единичек.
Формат входных данных
Первые две строки входного файла содержат числа N и M (соответственно ширина и высота изображения). Далее следуют M строк по N символов в каждой (1 или 0).
формат результата
Выходной файл должен содержать M строк по N символов в каждой, соответствующие исходному изображению.
Примеры
Входные |
6 |
3 |
011001 |
000100 |
110001 |
Выходные данные |
0aa00b |
000a00 |
сс000b |