Во-первых, ради производительности вы строите Map<String, List<String>>
нормализованных 4-буквенных комбинаций в список слов с этими 4 буквами.
например. climb
имеет 5 различных комбинаций из 4 букв, которые могут привести к слову: limb
, cimb
, clmb
, clib
и clim
. Поскольку порядок букв не имеет значения, вы нормализуете его, сортируя буквы: bilm
, bcim
, bclm
, bcil
и cilm
.
Каждое слово является узлом в вашем графе, и пять нормализованных 4-буквенных комбинаций являются доступными "портами" для ребер из других слов. Имея карту на месте, вы можете очень быстро найти все узлы слов, по которым вы можете перемещаться, от заданного узла, взяв последние 4 буквы, нормализовав их и посмотрев на карту (конечно, за исключением самого узла).
например. со словами climb
, blimp
и limps
вы получите следующую карту:
bcil -> climb
bcim -> climb
bclm -> climb
bilm -> climb, blimp
bilp -> blimp
bimp -> blimp
blmp -> blimp
cilm -> climb
ilmp -> blimp, limps
ilms -> limps
ilps -> limps
imps -> limps
lmps -> limps
Конечно, любая запись на карте, содержащая только одно слово в списке, является словом, указывающим на себя, поэтому они неактуальны и могут быть удалены. Хотя в коде нет особой причины делать это на самом деле, для удобства людей полезными являются следующие элементы карты:
bilm -> climb, blimp
ilmp -> blimp, limps
Теперь вы можете строить свои края:
climb --[bilm]-> blimp
blimp --[ilmp]-> limps
limps --[imps]->
Здесь не происходит BFS.
Построение карты: O (n) .
Создание узлов: O (n) .
Создание ребер: O (n * m) , где m
- среднее количество ребер на узел. Поскольку m
, вероятно, приближается к константе для больших графов, ее можно исключить.
Это означает, что общее решение составляет O (n) .