HexaRegex: Данина Мартіну Ендеру


37

Нещодавно Мартін Ендер досяг 100К і придумав кілька приголомшливих мов . Ми будемо трохи повеселитися з одним із них, Гексагоні (і трохи регексу для сітківки )

Як короткий огляд, вам потрібно написати програму, яка вводить шестикутну сітку і визначає, чи є шлях до цієї сітки, який відповідає рядку тексту

Генерація

Шестикутник генерує шестикутники з рядка тексту, використовуючи наступні кроки:

  1. Обчисліть мінімальний розмір шестикутника (візьміть довжину струни і округніть до найближчого шістнадцятирічного числа )
  2. Загортання тексту в шестикутник вищевказаного розміру
  3. Заповнення решти місць ..

Наприклад, рядок тексту abcdefghijklmвимагає шестикутник довжиною сторони 3, і тому стає:

   a b c
  d e f g
 h i j k l
  m . . .
   . . .

Тепер зауважте, що в шестикутнику можна подорожувати 6 можливих напрямків. Наприклад, у вищевказаному шестикутнику eприлягає доabfjid .

Загортання

Крім того, в шестикутнику загортаються шестикутники:

   . . . .          . a . .          . . f .          . a . .   
  a b c d e        . . b . .        . . g . .        . b . . f  
 . . . . . .      g . . c . .      . . h . . a      . c . . g . 
. . . . . . .    . h . . d . .    . . u . . b .    . d . . h . .
 f g h i j k      . i . . e .      . j . . c .      e . . i . . 
  . . . . .        . j . . f        k . . d .        . . j . .  
   . . . .          . k . .          . . e .          . k . .   

Якщо ви дивитесь на 2-й і 4-й приклад, помічайте, як aі як kзнаходяться в одних і тих же місцях, незважаючи на те, що ви загортаєтесь в різні боки. Завдяки цьому факту ці плями прилягають лише до 5 інших місць .

Щоб зробити це зрозумілішим:

   a b c d
  e f g h i
 j k l m n o
p q r s t u v
 w x y z A B
  C D E F G
   H I J K
  1. Краї обертаються до протилежного сусіда ( b->Iі G->j).
  2. Верхній / нижній кути загортають до протилежного центрального кута та вгору / вниз ( d->K,pі H->a,v).
  3. Центральні кути оберніть у верхній і нижній кути ( v->a,H)

Шляхи

Шлях , щоб бути послідовність сусідніх місцях , не повертаючись у тому ж місці.

   a b c
  d e f g
 h i f k l
  m . . .
   . . .

У вищевказаному шестикутнику aefkgm- дійсний шлях. Однак abfdце невірний шлях ( fі dне є суміжним) та abeaнедійсний (повертається до aмісця).

Ми можемо використовувати ці шляхи для узгодження тексту (наприклад, регулярного вираження) . Альфа-числовий символ відповідає собі (і тільки собі), і .відповідає будь-якому символу. Наприклад, шлях aej..lgmбуде відповідати aej..lgm, aejAAlgm, aeja.lgmабо aej^%gm.

Введення-виведення

Ваша програма повинна мати два рядки (у будь-якому порядку). Перший рядок буде не порожнім і складається лише з буквено-цифрових символів [a-zA-Z0-9]. Це буде представляти шестикутник, на якому ви працюєте. Другий рядок буде складатися з символів для друку.

Вам потрібно повернути триєдне значення iff, якщо в шестикутнику є шлях, який відповідає рядку заданого тексту, інакше хибне значення.

Тестові справи

Truthy:

"a","a"
"ab","a"
"ab","b"
"ab","ba"
"ab","aba"
"ab","&"
"ab","#7.J!"
"ab","aaaaaa"
"ab","bgjneta"
"ab","cebtmaa"
"abcdefg","dfabcg"
"AbCDeFG","GCbAeFD"
"aaaabbb","aaababb"
"abcdefghijklmnopqrs","alq"
"abcdefghijklmnopqrs","aqnmiedh"
"abcdefghijklmnopqrs","adhcgkorbefjimnqlps"
"11122233344455","12341345123245"
"abcdefgh","h%a"
"abcdefghijklm","a)(@#.*b"
"abcdefghijklm","a)(@#.*i"
"abcdefghij","ja"
"abcdefghijklmno","kgfeia"
"abcdefghijklmno","mmmmmiea"
"abcdefghijklmno","mmmmmlae"
"abcdefghijklmno","ja"
"abcdefghijklmnopqrs","eijfbadhmnokgcsrql"

Фальсі:

"a","b"
"a","%"
"a","."
"a","aa"
"a","a."
"ab","#7.J!*"
"ab","aaaaaaa"
"ab","aaaabaaa"
"ab","123456"
"abcdefg","bfgedac"
"abcdefg","gecafdb"
"abcdefg","GCbaeFD"
"aaaabbb","aaaaabb"
"abcdefghijklmnopqrs","aqrcgf"
"abcdefghijklmnopqrs","adhlcgknbeifjm"
"abcdefghijklmnopqrs","ja"
"abcdefghijklm","a)(@#.*&"
"abcdefghijklmno","a)(@bfeijk"
"abcdefghijklmno","kgfeic"
"abcdefghijklmno","mmmmmmiea"

Це , тому робіть свої відповіді якомога коротшими на улюбленій мові.


21
Хтось повинен це зробити в Гексагонії. : D
DJMcMayhem


9
Спочатку мене дуже збентежили непрості приклади, поки я не зрозумів, що шестикутник є джерелом виразів , так би мовити, не другого рядка. Що все ще згинає розум ...: P
El'endia Starman

5
@DrGreenEggsandIronMan я запропонує 500-респ щедрот , якщо хто - то робить це зробити в Hexagony.
AdmBorkBork

2
@Blue Важливим є приклад незаповненого шестикутника. Що ще важливіше, я зробив різницю між "стежкою" та "регулярною виразкою".
Натан Меррілл

Відповіді:


14

Сітківка , 744 байт

Вибачте, люди цього разу не шестикутники ...

Кількість байтів передбачає кодування ISO 8859-1.

.+¶
$.'$*_¶$&
^_¶
¶
((^_|\2_)*)_\1{5}_+
$2_
^_*
$.&$*×_$&$&$.&$*×
M!&m`(?<=(?=×*(_)+)\A.*)(?<-1>.)+(?(1)!)|^.*$
O$`(_)|.(?=.*$)
$1
G-2`
T`d`À-É
m`\A(\D*)(?(_)\D*¶.|(.)\D*¶\2)((.)(?<=(?<4>_)\D+)?((?<=(?<1>\1.)\4\D*)|(?<=(?<1>\D*)\4(?<=\1)\D*)|(?<=\1(.(.)*¶\D*))((?<=(?<1>\D*)\4(?>(?<-7>.)*)¶.*\6)|(?<=(?<1>\D*)(?=\4)(?>(?<-7>.)+)¶.*\6))|(?<=(×)*¶.*)((?<=(?<1>\1.(?>(?<-9>¶.*)*))^\4\D*)|(?<=(?<1>\D*)\4(?>(?<-9>¶.*)*)(?<=\1)^\D*)|(?<=(?<1>\1\b.*(?(9)!)(?<-9>¶.*)*)\4×*¶\D*)|(?<=(?<1>\D*\b)\4.*(?(9)!)(?<-9>¶.*)*(?<=\1.)\b\D*))|(?<=(?<1>\1.(?>(?<-11>.)*)¶.*)\4(.)*¶\D*)|(?<=(?<1>\1(?>(?<-12>.)*)¶.*)\4(.)*¶\D*)|(?<=(?<1>\1.(?>(?<-13>.)*¶\D*))\4(\w)*\W+.+)|(?<=(?<1>.*)\4(?>(?<-14>.)*¶\D*)(?<=\1.)(\w)*\W+.+))(?<=\1(\D*).+)(?<!\1\15.*(?<-1>.)+))*\Z

Очікує цільовий рядок у першому рядку та шестикутник у другому рядку введення. Друкує 0або 1відповідно.

Спробуйте в Інтернеті! (Перший рядок включає тестовий набір, де кожен рядок є тестовим випадком, використовуючи¦ для розділення замість передачі рядка.)

Правильний спосіб вирішити цю проблему - це, звичайно, підсумок. ;) І якби не те, що цей виклик також передбачає процедуру розгортання шестикутника , ця відповідь насправді складалася б із нічого, окрім одного 600-байтного довгого виразного вираження.

Це ще не зовсім оптимально для гольфу, але я цілком задоволений результатом (моя перша робоча версія, після видалення названих груп та інших речей, необхідних для розуму, становила близько 1000 байт). Я думаю, що я міг би зберегти близько 10 байтів, змінивши порядок рядка та шестикутник, але це потребує повного переписування регулярного виразу в кінці, чого я зараз не відчуваю. Існує також 2-байт економія, опустивши Gсцену, але це значно сповільнює рішення, тому я зачекаю, коли я внесу ці зміни, поки не переконаюся, що я переграв це так добре, як зможу.

Пояснення

Основна частина цього рішення широко використовує балансуючі групи , тому я рекомендую ознайомитися з ними, якщо ви хочете зрозуміти, як це працює в деталях (я не буду звинувачувати вас, якщо ви цього не зробите ...).

Перша частина рішення (тобто все, крім двох останніх рядків) - це модифікована версія моєї відповіді на Розгортання вихідного коду Гексагонії . Він конструює шестикутник, залишаючи цільовий рядок недоторканим (і він фактично будує шестикутник перед цільовим рядком). Я вніс деякі зміни до попереднього коду, щоб зберегти байти:

  • Фоновий символ ×замість пробілу, щоб він не вступав у конфлікт із потенційними пробілами на вході.
  • _Замість цього використовується символ неоперації / підстановки ., щоб комірки сітки можна було надійно ідентифікувати як символи слова.
  • Я не вставляю пробілів чи відступів після того, як шестикутник спочатку побудований. Це дає мені похилий шестикутник, але це насправді набагато зручніше працювати, а правила суміжності досить прості.

Ось приклад. Для наступного тестового випадку:

ja
abcdefghij

Ми отримуємо:

××abc
×defg
hij__
____×
___××
ja

Порівняйте це зі звичайною компоновкою шестикутника:

  a b c
 d e f g
h i j _ _
 _ _ _ _
  _ _ _

Ми можемо бачити, що сусідами зараз є всі звичні 8 сусідів Мура, за винятком північно-західних та південно-східних сусідів. Тому нам потрібно перевірити горизонтальну, вертикальну і південно-західну / північно-східну прилягання (ну а далі є краї обгортання). Використання цього більш компактного макета також має бонус, що ми зможемо використовувати ті, хто ××знаходиться в кінці, щоб визначити розмір шестикутника на льоту, коли нам це буде потрібно.

Після того, як ця форма була побудована, ми робимо ще одну зміну для всього рядка:

T`d`À-É

Це замінює цифри розширеними літерами ASCII

ÀÁÂÃÄÅÆÇÈÉ

Оскільки вони замінені як в шестикутнику, так і в цільовому рядку, це не вплине на відповідність рядка чи ні. Також, оскільки вони букви \wі \bдосі ідентифікують їх як шестикутні клітини. Перевага цієї заміни полягає в тому, що тепер ми можемо використовувати \Dв майбутньому регулярному виразі відповідність будь-яким символам (зокрема, стрічкам рядків, а також символам, що не входять в рядок). Ми не можемо скористатися цим sваріантом, оскільки нам потрібно .буде відповідати символам, які не передаються без ліній, у кількох місцях.

Тепер останній біт: визначення того, чи відповідає будь-який шлях нашому заданому рядку. Це робиться за допомогою одного жахливого виразного виразу. Ви можете запитати себе, чому?!?! Ну, це в основному проблема зворотного відстеження: ви починаєте десь і пробуєте шлях до тих пір, поки він відповідає рядку, і як тільки він не буде відслідковувати і спробувати іншого сусіда з останнього символу, який працював. Theодна річщо ви отримуєте безкоштовно під час роботи з регулярною вивізкою - це зворотній трек. Це буквально єдине, що робить движок-регекс. Отже, якщо ми просто знайдемо спосіб описати дійсний шлях (який досить складний для подібного роду проблем, але, безумовно, можливий для балансуючих груп), тоді двигун регулярного генерування розібрає пошук цього шляху серед усіх можливих для нас. Звичайно, можна було б здійснити пошук вручну з декількома етапами ( і я це робив раніше ), але я сумніваюся, що в цьому конкретному випадку він буде коротшим.

Одне питання щодо впровадження цього методу за допомогою регулярного вираження полягає в тому, що ми не можемо довільно переплести курсор двигуна регулярних вирівнювань назад і назад через рядок під час зворотного відстеження (що нам знадобиться, оскільки шлях може йти вгору або вниз). Отже, натомість ми відстежуємо власний "курсор" у групі захоплення та оновлюємо це на кожному кроці (ми можемо тимчасово переміщатися до положення курсору за допомогою пробігу). Це також дозволяє нам зберігати всі минулі позиції, які ми використовуватимемо для перевірки того, що раніше не відвідували поточну позицію.

Тож давайте до цього. Ось дещо безпечніша версія регулярного виразу, з названими групами, відступом, менш випадковим порядком сусідів та деякими коментарями:

\A
# Store initial cursor position in <pos>
(?<pos>\D*)
(?(_)
  # If we start on a wildcard, just skip to the first character of the target.
  \D*¶.
|
  # Otherwise, make sure that the target starts with this character.
  (?<first>.)\D*¶\k<first>
)
(?:
  # Match 0 or more subsequent characters by moving the cursor along the path.
  # First, we store the character to be matched in <next>.
  (?<next>.)
  # Now we optionally push an underscore on top (if one exists in the string).
  # Depending on whether this done or not (both of which are attempted by
  # the engine's backtracking), either the exact character, or an underscore
  # will respond to the match. So when we now use the backreference \k<next>
  # further down, it will automatically handle wildcards correctly.
  (?<=(?<next>_)\D+)?
  # This alternation now simply covers all 6 possible neighbours as well as
  # all 6 possible wrapped edges.
  # Each option needs to go into a separate lookbehind, because otherwise
  # the engine would not backtrack through all possible neighbours once it
  # has found a valid one (lookarounds are atomic). 
  # In any case, if the new character is found in the given direction, <pos>
  # will have been updated with the new cursor position.
  (?:
    # Try moving east.
    (?<=(?<pos>\k<pos>.)\k<next>\D*)
  |
    # Try moving west.
    (?<=(?<pos>\D*)\k<next>(?<=\k<pos>)\D*)
  |
    # Store the horizontal position of the cursor in <x> and remember where
    # it is (because we'll need this for the next two options).
    (?<=\k<pos>(?<skip>.(?<x>.)*¶\D*))
    (?:
      # Try moving north.
      (?<=(?<pos>\D*)\k<next>(?>(?<-x>.)*)¶.*\k<skip>)
    |
      # Try moving north-east.
      (?<=(?<pos>\D*)(?=\k<next>)(?>(?<-x>.)+)¶.*\k<skip>)
    )
  |
    # Try moving south.
    (?<=(?<pos>\k<pos>.(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)
  |
    # Try moving south-east.
    (?<=(?<pos>\k<pos>(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)
  |
    # Store the number of '×' at the end in <w>, which is one less than the
    # the side-length of the hexagon. This happens to be the number of lines
    # we need to skip when wrapping around certain edges.
    (?<=(?<w>×)*¶.*)
    (?:
      # Try wrapping around the east edge.
      (?<=(?<pos>\k<pos>.(?>(?<-w>¶.*)*))^\k<next>\D*)
    |
      # Try wrapping around the west edge.
      (?<=(?<pos>\D*)\k<next>(?>(?<-w>¶.*)*)(?<=\k<pos>)^\D*)
    |
      # Try wrapping around the south-east edge.
      (?<=(?<pos>\k<pos>\b.*(?(w)!)(?<-w>¶.*)*)\k<next>×*¶\D*)
    |
      # Try wrapping around the north-west edge.
      (?<=(?<pos>\D*\b)\k<next>.*(?(w)!)(?<-w>¶.*)*(?<=\k<pos>.)\b\D*)
    )
  |
    # Try wrapping around the south edge.
    (?<=(?<pos>\k<pos>.(?>(?<-x>.)*¶\D*))\k<next>(?<x>\w)*\W+.+)
  |
    # Try wrapping around the north edge.
    (?<=(?<pos>.*)\k<next>(?>(?<-x>.)*¶\D*)(?<=\k<pos>.)(?<x>\w)*\W+.+)
  )
  # Copy the current cursor position into <current>.
  (?<=\k<pos>(?<current>\D*).+)
  # Make sure that no matter how many strings we pop from our stack of previous
  # cursor positions, none are equal to the current one (to ensure that we use
  # each cell at most once).
  (?<!\k<pos>\k<current>.*(?<-pos>.)+)
)*
# Finally make sure that we've reached the end of the string, so that we've
# successfully matched all characters in the target string.
\Z

Я сподіваюся, що загальна думка з цього приблизно зрозуміла. Як приклад того, як працює один з цих рухів по шляху, давайте розглянемо біт, який переміщує курсор на південь:

(?<=(?<pos>\k<pos>.(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)

Пам'ятайте, що вигляд ззаду слід читати справа наліво (або знизу вгору), тому що це порядок, у якому вони виконуються:

(?<=
  (?<pos>
    \k<pos>       # Check that this is the old cursor position.
    .             # Match the character directly on top of the new one.
    (?>(?<-x>.)*) # Match the same amount of characters as before.
    ¶.*           # Skip to the next line (the line, the old cursor is on).
  )               # We will store everything left of here as the new 
                  # cursor position.
  \k<next>        # ...up to a match of our current target character.
  (?<x>.)*        # Count how many characters there are...
  ¶\D*            # Skip to the end of some line (this will be the line below
                  # the current cursor, which the regex engine's backtracking
                  # will determine for us).
)

Зауважте, що не потрібно ставити якір перед значком, \k<pos>щоб переконатися, що це насправді доходить до початку рядка. <pos>завжди починається з кількості, ×яку не можна знайти більше ніде, тому це вже є неявним якорем.

Я не хочу розкривати цю посаду більше, ніж потрібно, тому я не буду детально описуватись в інших 11 випадках, але в принципі всі вони працюють аналогічно. Ми перевіряємо, що <next>можна знайти в якомусь конкретному (допустимому) напрямку зі старого положення курсору за допомогою балансуючих груп, а потім зберігаємо рядок до цього збігу як нове положення курсору в <pos>.


13

Python 3, 990 943 770 709 байт

Перша відповідь, так!

EDIT: Складання списку суміжностей із гольфами. Зараз я використовую дещо іншу формулу

EDIT 2: Видалено зайвий пух, набагато більше гольфу.

EDIT 3: Скоротивши код для перетворення з індексу в список до координат, ще кілька речей.

Більшість байтів стосується складання списку суміжності (він має найбільший потенціал для гри в гольф). Відтоді це проста справа жорстокого примусового рішення (що я можу зробити в менших байтах).

Гольф:

from math import*
b=abs
c=max
e=range
f=len
A=input()
B=input()
C=ceil(sqrt((f(A)-.25)/3)+.5)
D=3*C*~-C+1
E=2*C-1
F=C-1
A+='.'*(D-f(A))
G=[set()for x in e(D)]
I=lambda H:sum(E+.5-b(t-F+.5)for t in e(int(H+F)))
for x in e(D):
 r=sum([[J-F]*(E-b(J-F))for J in e(E)],[])[x];q=x-I(r);s=-q-r;a=lambda q,r:G[x].add(int(q+I(r)));m=c(map(b,[q,r,s]))
 if m==F:
  if q in(m,-m):a(-q,-s)
  if r in(m,-m):a(-s,-r)
  if s in(m,-m):a(-r,-q)
 for K,L in zip([1,0,-1,-1,0,1],[0,1,1,0,-1,-1]):
  M,H=q+K,r+L
  if c(map(b,[M,H,-M-H]))<C:a(M,H)
def N(i,O,P):
 Q=O and O[0]==A[i]or'.'==A[i];R=0
 if(2>f(O))*Q:R=1
 elif Q:R=c([(x not in P)*N(x,O[1:],P+[i])for x in G[i]]+[0])
 return R
print(c([N(x,B,[])for x in e(D)])*(f(B)<=D))

Неозорені w / пояснення:

from math import*

#Rundown of the formula:
# * Get data about the size of the hexagon
# * Create lookup tables for index <-> coordinate conversion
#   * q=0, r=0 is the center of the hexagon
#   * I chose to measure in a mix of cubic and axial coordinates,
#     as that allows for easy oob checks and easy retrevial  
# * Create the adjacency list using the lookup tables, while
#   checking for wrapping
# * Brute-force check if a path in the hexagon matches the
#   expression

# shorten functions used a lot
b=abs
c=max
e=range

# Get input

prog=input()
expr=input()

# sdln = Side length
# hxln = Closest hexagonal number
# nmrw = Number of rows in the hexagon
# usdl = one less than the side length. I use it a lot later

sdln=ceil(sqrt((len(prog)-.25)/3)+.5)
hxln=3*sdln*~-sdln+1
nmrw=2*sdln-1
usdl=sdln-1

# Pad prog with dots

prog+='.'*(hxln-len(prog))

# nmbf = Number of elements before in each row
# in2q = index to collum
# in2r = index to row

nmbf=[0]*nmrw
in2q=[0]*hxln
in2r=[0]*hxln

#  4    5
#   \  /
# 3 -- -- 0
#   /  \ 
#  2    1

# dirs contains the q,r and s values needed to move a point
# in the direction refrenced by the index

qdir=[1,0,-1,-1,0,1]
rdir=[0,1,1,0,-1,-1]

# generate nmbf using a summation formula I made

for r in e(nmrw-1):
    nmbf[r+1]=int(nmbf[r]+nmrw+.5-b(r-sdln+1.5))

# generate in2q and in2r using more formulas
# cntr = running counter

cntr=0
for r in e(nmrw):
    bgnq=c(-r,1-sdln)
    for q in e(nmrw-b(r-sdln+1)):
        in2q[cntr]=bgnq+q
        in2r[cntr]=r-usdl
        cntr+=1

# adjn = Adjacency sets

adjn=[set()for x in e(hxln)]

# Generate adjacency sets

for x in e(hxln):
    #Get the q,r,s coords
    q,r=in2q[x],in2r[x]
    s=-q-r
    # a = function to add q,r to the adjacency list
    a=lambda q,r:adjn[x].add(q+nmbf[r+usdl])
    # m = absolute value distance away from the center
    m=c(map(b,[q,r,s]))
    # if we are on the edge (includes corners)...
    if m==usdl:
        # add the only other point it wraps to
        if q in(m,-m):
            a(-q,-s)
        if r in(m,-m):
            a(-s,-r)
        if s in(m,-m):
            a(-r,-q)
    # for all the directions...
    for d in e(6):
        # tmp{q,r,s} = moving in direction d from q,r,s
        tmpq,tmpr=q+qdir[d],r+rdir[d]
        # if the point we moved to is in bounds...
        if c(map(b,[tmpq,tmpr,-tmpq-tmpr]))<sdln:
            # add it
            a(tmpq,tmpr)

# Recursive path checking function
def mtch(i,mtst,past):
    # dmch = Does the place we are on in the hexagon match
    #        the place we are in the expression?
    # out = the value to return
    dmch=mtst and mtst[0]==prog[i]or'.'==prog[i]
    out=0
    # if we are at the end, and it matches...
    if(2>len(mtst))*dmch:
        out=1
    # otherwise...
    elif dmch:
        # Recur in all directions that we haven't visited yet
        # replace '*' with 'and' to speed up the recursion
        out=c([(x not in past)*mtch(x,mtst[1:],past+[i])for x in adjn[i]]+[0])
    return out

# Start function at all the locations in the hexagon
# Automatically return false if the expression is longer
# than the entire hexagon
print(c([mtch(x,expr,[])for x in e(hxln)])*(len(expr)<=hxln))

Так близько до Retina! :( Так , бий Ретіну!


5

Javascript (ES6), 511 500 496 байт

(H,N)=>{C=(x,y)=>(c[x]=c[x]||[])[y]=y;S=d=>(C(x,y=x+d),C(y,x),C(s-x,s-y),C(s-y,s-x));r=(x,p,v)=>{p<N.length?(v[x]=1,c[x].map(n=>!v[n]&&(H[n]==N[p]||H[n]=='.')&&r(n,p+1,v.slice()))):K=1};for(e=x=K=0;(s=3*e*++e)<(l=H.length)-1;);H+='.'.repeat(s+1-l);for(a=[],b=[],c=[[]],w=e;w<e*2;){a[w-e]=x;b[e*2-w-1]=s-x;for(p=w;p--;x++){w-e||S(s-e+1);w<e*2-1&&(S(w),S(w+1));p&&S(1)}a[w]=x-1;b[e*3-++w]=s-x+1}a.map((v,i)=>S(b[i]-(x=v)));[N[0],'.'].map(y=>{for(x=-1;(x=H.indexOf(y,x+1))>-1;r(x,1,[]));});return K}

Негольфірованний і прокоментував

// Entry point
//   H = haystack (the string the hexagon is filled with)
//   N = needle (the substring we're looking for)
(H, N) => {
  // C(x, y) - Helper function to save a connection between two locations.
  //   x = source location
  //   y = target location
  C = (x, y) => (c[x] = c[x] || [])[y] = y;

  // S(d) - Helper function to save reciprocal connections between two locations
  //        and their symmetric counterparts.
  //   d = distance between source location (x) and target location
  S = d => (C(x, y = x + d), C(y, x), C(s - x, s - y), C(s - y, s - x));

  // r(x, p, v) - Recursive path search.
  //   x = current location in hexagon
  //   p = current position in needle
  //   v = array of visited locations
  r = (x, p, v) => {
    p < N.length ?
      (v[x] = 1, c[x].map(n => !v[n] && (H[n] == N[p] || H[n] == '.') &&
      r(n, p + 1, v.slice())))
    :
      K = 1
  };

  // Compute e = the minimum required edge width of the hexagon to store the haystack.
  // Also initialize:
  //   x = current location in hexagon
  //   l = length of haystack
  //   s = size of hexagon (number of locations - 1)
  //   K = fail/success flag
  for(e = x = K = 0; (s = 3 * e * ++e) < (l = H.length) - 1;);

  // Pad haystack with '.'
  H += '.'.repeat(s + 1 - l);

  // Build connections c[] between locations, using:
  //   x = current location
  //   w = width of current row
  //   p = position in current row
  // Also initialize:
  //   a[] = list of locations on top left and top right edges
  //   b[] = list of locations on bottom left and bottom right edges
  for(a = [], b = [], c = [[]], w = e; w < e * 2;) {
    a[w - e] = x;
    b[e * 2 - w - 1] = s - x;

    for(p = w; p--; x++) {
      // connection between top and bottom edges
      w - e || S(s - e + 1);
      // connections between current location and locations below it
      w < e * 2 - 1 && (S(w), S(w + 1));
      // connection between current location and next location
      p && S(1)
    }
    a[w] = x - 1;
    b[e * 3 - ++w] = s - x + 1
  }

  // Save connections between top left/right edges and bottom left/right edges.
  a.map((v, i) => S(b[i] - (x = v)));

  // Look for either the first character of the needle or a '.' in the haystack,
  // and use it as the starting point for the recursive search. All candidate
  // locations are tried out.
  [N[0], '.'].map(y => {
    for(x = -1; (x = H.indexOf(y, x + 1)) > -1; r(x, 1, []));
  });

  // Return fail/success flag.
  return K
}

Тестові справи

Розріз нижче буде проглянути всі пробні та хибні тести.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.