максимально возможный прямоугольник букв
напишите программу, чтобы найти максимально возможный прямоугольник букв, так что каждая строка образует слово (слева направо) и каждый столбец образует слово (сверху вниз).
Я нашел этот интересный вопрос. Это не домашнее задание, хотя может звучать именно так. (Я не в школе). Я делаю это ради забавы.
пример
С кошки, автомобиль, обезьяна, api, rep, совет получаем следующий прямоугольник (который является квадратом):
c a r a p e t i p
моя первоначальная идея-построить какое-то дерево префиксов, чтобы я мог получить все слова, начинающиеся с определенной строки. Это было бы полезно, когда у нас уже есть 2 или более слов (как читать сверху вниз или слева направо) и нам нужно найти второе слово добавить.
любой другой идеи?
редактировать
можно ли это сделать с помощью кубоида (3D прямоугольника)?
что, если ему нужно иметь действительные слова на диагоналях (IDEA credit: user645466); как будет оптимизирован algo для него?
3 ответов
пусть D-словарь. Исправить m и n. Мы можем сформулировать задачу нахождения прямоугольника m × n как проблема удовлетворения ограничений (CSP).
xi, 1 ... xi, n ∈ D ∀i {{1,..., m}
x1, j ... xm, j ∈ D ∀j {{1,..., n}
xi, j ∈ {A,..., Z} {i {{1,..., m}, j j {{1,..., n}
очень распространенным подходом к решению CSPs является чтобы совместить backtracking с распространением ограничений. Грубо говоря, backtracking означает, что мы выбираем переменную, угадываем ее значение и рекурсивно решаем подзадачу с одной меньшей переменной, а распространение ограничений означает попытку уменьшить количество возможностей для каждой переменной (возможно, до нуля, что означает, что нет решения).
в качестве примера мы могли бы начать сетку 3 × 3, выбрав x1,1 = Q
.
Q??
???
???
с английским словарь, единственная возможность для x1,2 и x2,1 is U
(в перед Scrabble "слова").
искусство решения CSPs балансирует между обратным отслеживанием и распространением ограничений. Если мы вообще не распространяем ограничения, то мы просто используем грубую силу. Если мы прекрасно распространяем ограничения, то нам не нужно отступать, но алгоритм распространения, который сам по себе решает NP-трудную проблему, вероятно, довольно дорог для бежать.
в этой проблеме работа с большим словарем на каждом узле отслеживания будет стоить дорого, если у нас нет хорошей поддержки структуры данных. Я опишу подход, который использует trie или чувак быстро вычислить набор букв, через которые префикс распространяется на полное слово.
на каждом обратном узле набор переменных, которые мы назначили, является молодой таблицей. Другими словами, ни одна переменная не назначается до тех пор, пока переменные над ним и слева были назначены. На приведенной ниже диаграмме .
обозначает присвоенную переменную и *
и ?
обозначим неинициализированные переменные.
.....*
...*??
...*??
..*???
*?????
переменные, отмеченные *
являются кандидатами для следующего, которому будет присвоено значение. Преимущество множественного выбора, а не Выбора фиксированной переменной каждый раз заключается в том, что некоторые упорядочения переменных могут быть намного лучше, чем другие.
для каждого *
, сделайте два поиска в trie / DAWG, один для горизонтального и один для вертикального, и вычислить пересечение наборов букв, которые могут прийти далее. Одна из классических стратегий-выбрать переменную с наименьшими возможностями в надежде, что мы быстрее достигнем противоречия. Мне нравится эта стратегия, потому что она естественно сокращается, когда есть переменная с нулевыми возможностями, и естественно распространяется, когда есть переменная с одной. Кэшируя результаты, мы можем сделать оценку каждого узла очень быстрой.
учитывая словарь слов заданной длины, создайте два новых словаря: первый содержит все однобуквенные префиксы слов (все буквы, которые могут быть первой буквой слова заданной длины), а второй содержит все двухбуквенные префиксы слов (все последовательности из двух букв, которые могут быть первыми двумя буквами слова заданной длины). Вы также можете делать тройные префиксы, но вам, вероятно, не нужно будет выходить за рамки что.
выберите слово из словаря, назовем его
X
. Это будет первая строка матрицы.проверяем, что
X[1], X[2], ..., X[N]
все однобуквенные префиксы, используя этот удобный список, который вы сделали. Если это так, перейдите к шагу 3; в противном случае вернитесь к шагу 1.выберите слово из словаря, назовем его
Y
. Это будет вторая строка матрицы.-
Регистрация это
X[1] Y[1]
,X[2] Y[2]
, ...,X[N] Y[N]
все действительные двойные буквенные префиксы, используя этот удобный список, который вы сделали. Если это так, перейдите к шагу 5; в противном случае вернитесь к шагу 3. Если это было последнее слово в словаре,то вернитесь к шагу 1....
2 (N-1). Выберите слово из словаря, назовите его
Z
. Это будет N-я строка матрицы.2N. Проверьте это
X[1] Y[1] ... Z[1]
,X[2] Y[2] ... Z[2]
, ...,X[N] Y[N] ... Z[N]
все слова в словаре. Если поздравляю, вы сделали это! В противном случае вернитесь к Шагу 2(N-1). Если это было последнее слово в словаре, то вернитесь к Шагу 2(n-3).
логика заключается в создании прямоугольника слов по одной строке за раз, выбирая слова для строк, а затем проверяя, что столбец мог бы завершите словами. Это будет происходить намного быстрее, чем добавление одной буквы за раз.
создайте сумку[] для word одинаковой длины = индекс затем создайте массив попыток, один три для списка слов каждой длины
Rectangle makeRectangle(length, height, rectangle)
{
if ( length == rectangle.length()) check if complete and return;
checkIfPartialComplete - check columns for valid prefixes
for ( i from 1 to grouplist[i-1])
{
newRectangle = append word[i] to rectangle
return makeRectangle(l,h, rectangle with appended word) if not equal to null
}
}
boolean checkPartial(int l, Trie trie)
{
for ( int i =0 ; i < l; i++)
{
String col = getColumn(i);
if (!trie.contains(col))
{
return false;
}
}
return true;
}
boolean checkComplete(int l, Bag<String> lengthGroup)
{
for ( int i=0; i < l ; i++)
{
String col = getColumn(i);
if (!lengthGroup.contains(col))
{
return false;
}
}
return true;
}