Это настоящее дерево?


20

Вы должны написать программу или функцию, которая получает строку в качестве входных данных и выводит или возвращает, если входными данными является дерево ASCII.

  _
\/  /
 \_/
  |
  |

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

Непробельные символы соединяют две граничные точки своих ячеек отрезком линии:

  • / соединяет нижний левый и верхний правый углы
  • \ соединяет нижний правый и верхний левый углы
  • | соединяет средние точки нижнего края и верхнего края
  • _ соединяет нижний левый и нижний правый углы и среднюю точку нижнего края

(Обратите внимание, что это означает, что |может соединяться только с |или, _но не с /или \.)

Изображение ASCII называется деревом, если применяются следующие правила:

  • Ровно одна точка (корень) ровно одного символа касается нижнего края последнего ряда.
  • Вы можете добраться до любой точки любого отрезка по:

    • начиная с корня
    • используя только отрезки
    • никогда не идет в направлении вниз (даже в сторону вниз)

вход

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

    • Нет лишних пробелов вокруг дерева (как видно из примеров).
    • Нет лишних пробелов вокруг дерева (как видно в примерах), кроме пробелов с правой стороны строк, чтобы все строки имели одинаковую длину.
  • Трейлинг новой строки не является обязательным.

Выход

  • Непротиворечивое истинное значение, если вход является деревом 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~

Ungolfed, устаревшая версия (для личного просмотра):

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.