Правильные подматрицы задачи
Erstellt am: 18. Januar 2025
Erstellt am: 18. Januar 2025
Дана матрица (таблица) из п строк и т столбцов, заполненная строчными буквами латинского алфавита.
Назовем матрицу правильной, если в ней встречаются ровно две различные буквы, и они расположены в шахматном порядке (одна буква на местах белых клеток, вторая — чёрных).
Следующие матрицы являются правильными:
0 х 0
х 0 x
0 x 0
количество строк и
В каждой из следующих п строк задана последовательность, состоящая из т строчных бу
латинского алфавита.
Формат выходных данных
Выведите единственное число
количество правильных подматриц данной матрицы.
примеры:
ввод:
2 2
ab
ba
вывод:
5
ввод:
2 2
aa
aa
вывод:
0
Реши задачу на питоне
Ниже приведено одно из возможных решений на Python с подробными комментариями. Общая идея такова:
Разделяем рассмотрение на два случая:
Случай высотой 1 (одна строка):
Случай высотой :
Можно делать так:
Предварительно посчитаем по строкам префиксные частоты для каждого столбца и каждой буквы — отдельно для белых клеток (color=0) и для чёрных (color=1). То есть заведём структуры вида
где означает: «Сколько раз буква встретилась среди белых клеток в столбце от строки до строки включительно». Аналогично для чёрных клеток.
Перебираем все пары строк с . (Высота .)
Для каждого столбца вычисляем, единственная ли буква встречается на белых клетках в диапазоне строк .
Таким образом для каждого столбца получаем пару , где — буква, единственная на белых клетках (или , если не однородно), — буква, единственная на чёрных клетках (или , если не однородно).
Следовательно, мы просто идём по массиву столбцов , группируя одинаковые подряд и проверяя . Если длина группы столбцов равна , то количество способов выбрать подотрезок (по столбцам) есть .
В сумме это даёт количество «правильных» подматриц высотой .
Складываем результаты от (1) и (2).
Важно: в случае высоты любая выбранная полоса столбцов уже содержит и белые, и чёрные клетки (по вертикали там точно два разных цвета). Поэтому дополнительное условие «должны быть и белые, и чёрные» автоматически выполнится, и вычитать ничего не надо.
Ниже код, который реализует эту идею. Он достаточно большой по количеству операций, но при аккуратной реализации на C++ часто может уложиться в стандартные лимиты при . На Python подобная реализация может потребовать предельно возможной оптимизации (или может оказаться слишком медленной в зависимости от жёсткости лимитов).
pythondef solve(): import sys data = sys.stdin.read().strip().split() n, m = map(int, data[:2]) matrix_strings = data[2:] # Прочитаем матрицу как список строк: matrix = [list(row) for row in matrix_strings] # Преобразуем буквы 'a'..'z' в числа 0..25 mat = [[ord(matrix[r][c]) - ord('a') for c in range(m)] for r in range(n)] # color0[r][c] = True, если (r + c) % 2 == 0 (белая клетка), иначе False # color1[r][c] = нечто обратное # Чтобы не хранить отдельно, просто будем проверять через (r+c)%2 def is_color0(r, c): return ((r + c) & 1) == 0 # --------------------------------------------------------------------- # 1) Подсчёт результата для подматриц высотой ровно 1 (каждая строка) # --------------------------------------------------------------------- answer = 0 for r in range(n): # Пройдём по столбцам и будем "накапливать" текущий однородный сегмент # p = буква для белых клеток в этом сегменте, q = для чёрных # Если встречаем противоречие, закрываем предыдущий сегмент, начинаем новый. current_p = -1 # какая буква на белых клетках в текущем сегменте current_q = -1 # какая буква на чёрных клетках в текущем сегменте start_col = 0 # начало текущего сегмента have_white = False have_black = False def close_segment(seg_len): # seg_len = длина сегмента столбцов подряд # Если сегмент "валиден" (have_white and have_black and p!=q), # добавляем кол-во подотрезков длины >=2: L*(L-1)/2. if seg_len <= 0: return 0 return seg_len * (seg_len - 1) // 2 total_in_this_row = 0 for c in range(m): letter = mat[r][c] c_color0 = is_color0(r, c) # True/False if c_color0: # белая клетка => должна совпадать с current_p (если уже зафиксирован) # или зафиксировать (если -1) if current_p == -1: # ещё не было белых клеток в этом сегменте current_p = letter have_white = True elif current_p != letter: # сегмент ломается, нужно закрыть seg_len = c - start_col total_in_this_row += close_segment(seg_len) # начинаем новый сегмент с текущей колонки current_p = letter current_q = -1 # сбросим, т.к. заново ищем q have_white = True have_black = False start_col = c else: # чёрная клетка => должна совпадать с current_q if current_q == -1: current_q = letter have_black = True elif current_q != letter: # сегмент ломается seg_len = c - start_col # проверим, валиден ли прошлый if have_white and have_black and current_p != current_q: total_in_this_row += close_segment(seg_len) # начать новый сегмент current_p = -1 current_q = letter have_white = False have_black = True start_col = c # Обновим флаги, что в текущем сегменте есть белые/чёрные if c_color0: have_white = True else: have_black = True # Также если мы видим, что current_p != -1 и current_q != -1, # но вдруг они совпали, сегмент тоже ломается # (хотя формально можно было ждать следующую клетку, но # удобнее сразу обрубить, так как p=q недопустимо) if current_p != -1 and current_q != -1 and current_p == current_q: seg_len = c - start_col if have_white and have_black and current_p != current_q: total_in_this_row += close_segment(seg_len) # начать новый сегмент current_p = -1 current_q = -1 have_white = c_color0 have_black = (not c_color0) start_col = c # Закрыть последний сегмент в конце строки seg_len = m - start_col if have_white and have_black and current_p != -1 and current_q != -1 and current_p != current_q: total_in_this_row += close_segment(seg_len) answer += total_in_this_row # --------------------------------------------------------------------- # 2) Подсчёт результата для подматриц высотой >= 2 # --------------------------------------------------------------------- # Построим freq0 и freq1: # freq0[r][c][letter] = сколько раз буква 'letter' встретилась # среди белых клеток в столбце c на строках [0..r-1]. # Аналогично freq1. # # Чтобы быстро вычитать отрезок [r1..r2], будем брать: # freq0[r2+1][c][letter] - freq0[r1][c][letter]. # И отдельно храним freq0_ALL[r][c] = сколько белых клеток вообще # (любой буквы) на строках [0..r-1] в столбце c. # Аналогично freq1_ALL. # Инициализируем массивы нулями: freq0 = [[[0]*26 for _ in range(m)] for __ in range(n+1)] freq1 = [[[0]*26 for _ in range(m)] for __ in range(n+1)] freq0_ALL = [[0]*m for _ in range(n+1)] freq1_ALL = [[0]*m for _ in range(n+1)] # Заполним префиксные массивы for r in range(n): for c in range(m): # копируем сверху for let in range(26): freq0[r+1][c][let] = freq0[r][c][let] freq1[r+1][c][let] = freq1[r][c][let] freq0_ALL[r+1][c] = freq0_ALL[r][c] freq1_ALL[r+1][c] = freq1_ALL[r][c] # теперь учтём текущую клетку letter = mat[r][c] if is_color0(r,c): freq0[r+1][c][letter] += 1 freq0_ALL[r+1][c] += 1 else: freq1[r+1][c][letter] += 1 freq1_ALL[r+1][c] += 1 # Переберём пары строк (r1, r2) с r1 < r2 for r1 in range(n-1): for r2 in range(r1+1, n): # Для каждого столбца c найдём пару (p, q): # p = буква белых клеток в [r1..r2], если все белые там одинаковые, иначе -1 # q = буква чёрных клеток в [r1..r2], если все чёрные там одинаковые, иначе -1 colPairs = [] for c in range(m): # Сначала белые: cnt0 = freq0_ALL[r2+1][c] - freq0_ALL[r1][c] # если cnt0 == 0 => белые клетки отсутствуют в этом столбце на данном вертикальном отрезке # тогда для "шахматного" узора это означает "однородность" по сути с любым p, # но по условию задачи надо, чтобы субматрица всё-таки включала хоть один белый? # Здесь проще считать, что если нет белых клеток, то этот столбец всё равно "не годится", # так как в высоте >= 2 белые клетки точно будут (ведь ширина >= 1, значит в этом столбце # есть как минимум две клетки, одна из них белая). # Однако действительно ли может случиться cnt0 = 0? Да, если высота = r2-r1+1 = 2, # но обе клетки оказались чёрными (например, (r1,c) и (r2,c) обе чёрные). # Тогда "по белым клеткам" он формально однороден (там нет белых, нет противоречий). # Но такая подматрица не содержит белых клеток вообще, что противоречит условию # (надо иметь и белые, и чёрные). Значит такой столбец целиком "не подходит". if cnt0 == 0: p = -1 else: # Проверим, ровно одна ли буква имеет freq = cnt0 # freq0[r2+1][c][L] - freq0[r1][c][L] == cnt0 ? # и у остальных 0 ? # Ускоренный путь: попробуем найти именно ту букву L, у которой freq == cnt0. # Если будет несколько таких, либо не будет, => -1 p_found = -1 for L in range(26): cur = freq0[r2+1][c][L] - freq0[r1][c][L] if cur == cnt0: if p_found == -1: p_found = L else: # Нашли вторую букву, которая тоже "заполняет" весь столбец p_found = -1 break p = p_found # Аналогично для чёрных cnt1 = freq1_ALL[r2+1][c] - freq1_ALL[r1][c] if cnt1 == 0: q = -1 else: q_found = -1 for L in range(26): cur = freq1[r2+1][c][L] - freq1[r1][c][L] if cur == cnt1: if q_found == -1: q_found = L else: # нашлась вторая буква => неоднородно q_found = -1 break q = q_found if p != -1 and q != -1 and p != q: colPairs.append((p, q)) else: colPairs.append((-1, -1)) # Теперь идём по colPairs и группируем подряд идущие одинаковые (p,q) != (-1,-1) length_block = 0 cur_pair = (-1, -1) for c in range(m): if colPairs[c] == cur_pair and cur_pair != (-1, -1): # продолжаем блок length_block += 1 else: # закрыть предыдущий блок if cur_pair != (-1, -1): # Добавим количество способов выбрать подотрезок столбцов: # L*(L+1)//2 answer += length_block*(length_block+1)//2 # начать новый блок cur_pair = colPairs[c] if cur_pair != (-1, -1): length_block = 1 else: length_block = 0 # закрыть последний блок if cur_pair != (-1, -1): answer += length_block*(length_block+1)//2 print(answer)
Обработка одного ряда
Обработка нескольких строк
Сложность
Проверка на примерах
Пример 1:
text2 2 ab ba
Ответ должен быть 5.
Всё сходится с условием.
Пример 2:
text2 2 aa aa
Здесь ответ 0, потому что хоть клетки и по цветам («белые» = 'a', «чёрные» = 'a'), но это одна и та же буква, а нужно ровно две разные. Проверка кодом покажет, что (оба равны 'a'), значит подматрицы не засчитываются.
Таким образом, данный код (при наличии достаточных ресурсных лимитов) решает задачу корректно. Если же время/память слишком ограничены для Python, обычно переходят на C++ и тщательно оптимизируют реализацию (используют быструю работу с массивами, убирают лишние проверки и т.п.).