Це справжнє дерево?


20

Ви повинні написати програму або функцію, яка отримує рядок як вхід і вихід або повертається, якщо вхід є деревом ASCII.

  _
\/  /
 \_/
  |
  |

Дерева ASCII складаються з символів / \ | _ spacesі newlines.

Символи, що не містять пробілів, з'єднують дві крайові точки їх комірок лінійним відрізком:

  • / з'єднує нижній лівий і верхній правий кути
  • \ з'єднує нижній правий і верхній лівий кути
  • | з'єднує середні точки нижнього краю та верхнього краю
  • _ з'єднує нижній лівий і нижній правий кути і середню точку нижнього краю

(Зверніть увагу , що це означає , що |можна підключити тільки |або , _але не з /або \.)

Зображення ASCII називається деревом, якщо застосовуються такі правила:

  • Рівно одна точка (корінь) рівно одного символу торкається нижнього краю останнього ряду.
  • Ви можете дістатися до будь-якої точки будь-якого сегмента лінії:

    • починаючи від кореня
    • використовуючи лише відрізки рядків
    • ніколи не йдучи вниз (навіть не вниз)

Вхідні дані

  • Рядок, що складається з символів / \ | _ spaceі newlineмістить принаймні один символ, який не є пробілом.
  • Ви можете вибрати два вхідні формати:

    • Немає зайвого пробілу навколо дерева (як видно з прикладів).
    • Немає зайвого пробілу навколо дерева (як це показано в прикладах), крім пробілів у правій частині рядків, щоб усі рядки були однакової довжини.
  • Новий рядок не є обов'язковим.

Вихідні дані

  • Послідовне значення truthy, якщо вхід є деревом ascii.
  • Послідовне хибне значення, якщо вхід не є деревом ascii.

Приклади

Дійсні дерева:

|
  _
\/  /
 \_/
  |
  |
/ /    \/
\ \____/
 \/
 /
/
 \___/
 /   \
 \___/
   |
   |
   __/
 _/
/
____
  \  ___
 \ \/
  \/\_____/
 \/  \/
  \__/
    |
    |

Недійсні дерева (з додатковими поясненнями, які не є частинами даних):

\/
 \_______/
  \__   /
   | \_/    <- reachable only on with downward route
   |
_           <- multiple roots
\/          <- multiple root characters
/\          <- multiple roots
|           <- unreachable part

|
 __/
/           <- unreachable parts
|
\____/
 |  |       <- multiple roots
_\__/       <- unreachable parts (_ and \ don't connect to each other)
|

Це кодовий гольф, тому найкоротший запис виграє.

Відповіді:


7

ПМА / Равлики , 99 93

Друкує 1, якщо він відповідає визначенню "дерево" або 0, якщо ні. Переважна прямокутна форма вкладеної пробілом форми, хоча вона коштує лише одного байта (використовуючиF параметр) для перетворення розірваної версії в заповнений пробілом прямокутник, що було корисно при тестуванні.

&
\ |{(\_|\|)d=\||(\\a7|\/d|\_da7)=\\|(\\d|\/a5|\_da5)=\/|(\_lr|\|d|\/l|\\r)=\_},^_!(r.,^ )d~

Не застаріла версія (для мого особистого задоволення від перегляду):

F&
\ |
{
  \_ (lr=\_|da5=\/|da7=\\|d=\|) | \/ (l=\_|a5=\/|d=\\) | 
    \\ (r=\_|a7=\\|d=\/) | \|d=(\_|\|)    
}, 
^_ !(r.,^ ) d~

Це виявляється досить добре підходить до сучасних мовних особливостей. На жаль, мені довелося провести кілька годин, переслідуючи помилку підрахунку помилок, перш ніж вона могла працювати.

&Опція означає , що матч повинен досягти успіху в кожному персонажі. З кожної непробільної вихідної точки він перевіряє шлях вниз до низу. Створення кінцевої машини з регексом, на щастя, набагато коротше, використовуючи твердження, ось =... У нижньому рядку він перевіряє, чи немає праворуч символів.


10

Mathematica, 345 300 байт

Ще досить довго, але я думаю, це початок ...

(s=StringSplit;v=Reverse;#=="|"||#=="\\"||#=="/"&[""<>s@#]&&(g={};i=0;(c={i,++j};d={i,j+1/2};e=2d-c;g=Join[g,Switch[#,"|",{d->{1,0}+d},"/",{c->c+1},"\\",{e->{i+1,j}},"_",{c->d,d->e,e->c},_,{}]])&/@Characters[++i;j=0;#]&/@{##};Sort@VertexOutComponent[Graph@g,g[[1,1]]]==Union@@List@@@g)&@@v@s[#,"
"])&

Ось злегка незворушена версія:

(
  s = StringSplit;
  v = Reverse;
  # == "|" || # == "\\" || # == "/" &[
      "" <> s@#
      ] && (
      g = {};
      i = 0;
      (
           c = {i, ++j};
           d = {i, j + 1/2};
           e = 2 d - c;
           g = Join[g, Switch[#,
              "|", {d -> {1, 0} + d},
              "/", {c -> c + 1},
              "\\", {e -> {i + 1, j}},
              "_", {c -> d, d -> e, e -> c},
              _, {}
              ]]
           ) & /@ Characters[
          ++i;
          j = 0;
          #
          ] & /@ {##};
      Sort@VertexOutComponent[Graph@g, g[[1, 1]]] == 
       Union @@ List @@@ g
      ) & @@ v@s[#, "\n"]
) &

Це визначає неназвану функцію, яка приймає рядок як вхідний і повертає Trueабо False.

Основна ідея спочатку перевірити, чи є один корінь, а потім створити фактичний (спрямований) Graphоб’єкт, щоб перевірити, чи всі вершини можна досягти з кореня. Ось як ми будуємо графік:

Уявіть цілу сітку, накладену на графіку ASCII, де цілі координати відповідають куточкам комірок символів. Потім на кожній з комірок є шість відповідних точок, які можна з'єднати. Ось приклад, де я також позначені точки , aщоб f:

     |                 |
     |                 |
---(2,3)---(2.5,3)---(3,2)---
     | d      e      f |
     |                 |
     |                 |
     |                 |
     |                 |
     |                 |
     |                 |
     | a      b      c |
---(2,2)---(2.5,2)---(3,2)---
     |                 |
     |                 |

Таким чином, ми можемо побудувати графік, вершинами якого є ці напів цілі координати, а ребра яких визначаються символами, які не є пробілами. |підключається bдо e, /підключається aдо fі \підключається cдо d. Зверніть увагу, що вони повинні бути спрямовані по краях, щоб гарантувати, що ми ніколи не рухатимемося вниз під час переходу графіку пізніше. Для _ми можемо йти в будь-якому випадку, так що в теорії ми повинні чотири ребра a -> b, b -> a, b -> c, c -> b. Тим НЕ менше, ми можемо помітити , що всі , що має значення в тому , що є цикл , що містить всі три вершини, так що ми можемо скоротити це до трьох країв: a -> b, b -> c, c -> a.

Побудувати цей графік в Mathematica досить просто, тому що будь-який об’єкт може виступати вершиною, тому я можу насправді побудувати графік, вершини якого є координатними парами.

Нарешті, ми перевіряємо, що кожну вершину можна дістати з кореня. Координату кореня легко знайти як першу вершину, яку ми додали до графіка. Тоді найкоротший спосіб, який я знайшов, щоб перевірити, чи можна досягти всіх вершин, - це перевірити, чи VertexOutComponentкореня (тобто набір усіх вершин, доступних від кореня) ідентичний набору всіх вершин на графіку.


1
300 байт може бути довгим, але рівно 300 так задовольняє!
Олексій А.

2

Рубін 226 227 228

->i{w=i.index(?\n)+1
t=[i.index(/[^ _] *\n\z/)]
a=->x,c{(i[x]==c||i[x]==?_)&&t<<x}
((x=t.pop)&&(s=x-w;c=i[x])<?0?(a[s+1,?/];a[s,?\\]):c<?]?(a[s-1,?\\];a[s,?/]):c<?`?(a[x-1,?\\];a[x+1,?/]):a[s,?|]
i[x]=' ')while t!=[]
!i[/\S/]}

Інтернет-тест: http://ideone.com/Z7TLTt

Програма виконує наступні дії:

  • шукає кореневу папку ( \, /або| на останньому рядку)
  • Починаючи з цього кореня, піднімайтеся на дерево, користуючись правилами і замінюючи кожен відвіданий графік пробілом
  • врешті-решт, перевірте, чи повністю розміщена наша рядок з пробілів (мається на увазі дійсне дерево), чи ні (неправильне дерево; не всі фрагменти були "відвідані")

Ось це невольф:

F =-> input {
  row_size = input.index(?\n)+1

  root_coord = input.index /[^ _] *\n\z/

  # coordinates to process
  todo = [root_coord]

  add_todo = -> coord, char{
    if input[coord] == char || input[coord] == ?_
      todo << coord
    end
  }

  while todo.any?
    x = todo.pop

    next unless x # exit quickly if no root present

    case input[x]
    when ?|
      add_todo[x - row_size, ?|]
    when ?_
      add_todo[x - 1, ?\\]
      add_todo[x + 1, ?/]
    when ?/
      add_todo[x - row_size + 1, ?/]
      add_todo[x - row_size, ?\\]
    when ?\\
      add_todo[x - row_size - 1, ?\\]
      add_todo[x - row_size, ?/]
    end
    input[x]=' '
  end
  input.strip < ?*
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.