Правильные скобочные последовательности — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 14: Строка 14:
  
 
*"" (пустая строка) есть правильная скобочная последовательность;
 
*"" (пустая строка) есть правильная скобочная последовательность;
*правильная скобочная последовательность, взятая в скобки есть правильная скобочная последовательность;(*)
+
*пусть $S$ - правильная скобочная последовательность, тогда $(S)$ есть правильная скобочная последовательность;
*правильная скобочная последовательность, к которой приписана слева или справа другая правильная скобочная последовательность, есть правильная скобочная последовательность;
+
*пусть $S$ - правильная скобочная последовательность,тогда $()S$ и $S()$ есть правильные скобочные последовательности;
 
}}
 
}}
 
''Примеры правильных скобочный последовательностей:''
 
''Примеры правильных скобочный последовательностей:''
Строка 27: Строка 27:
 
''псевдокод'':
 
''псевдокод'':
  
   function check(s: string): boolean;
+
   bool check(s):
     var
+
     pointer = 0
      i, a :integer;
+
    for (i = 1; i < length(s) + 1; i++):
      begin
+
      pointer = (s[i] == '(')? pointer++ : pointer--
        a := 0
+
      if (pointer < 0)
        for i := 1 to length(s) do  {перебираем последовательно все символы строки (подразумевается, что в ней нет символов отличных от "(" и ")")}
+
      return false  
          begin
+
    if (pointer = 0)
            if s[i] = '(' then  {проверяем символ и производим соответствующие действия над переменной a}
+
    return true
            inc(a)
+
    else
            else
+
    return false
            dec(a);
 
            if a < 0 then
 
            check := false
 
          end;
 
        if a = 0 then  {проверяем на равенство нулю}
 
        check := true
 
        else
 
        check := false;
 
      end;
 
  
 
Надо отметить что, скобочные последовательности могут состоять не только из одного типа скобок, при этом недопустимо такое расположение, когда один тип скобок закрывает другой:
 
Надо отметить что, скобочные последовательности могут состоять не только из одного типа скобок, при этом недопустимо такое расположение, когда один тип скобок закрывает другой:
Строка 58: Строка 49:
 
== Лексикографический порядок порядок правильных скобочных последовательностей ==
 
== Лексикографический порядок порядок правильных скобочных последовательностей ==
  
Для того чтобы определить лексикографический порядок для правильных скобочных последовательностей будем интерпретировать открывающуюся скобку как "$0$", а закрывающуюся как "$1$" (**). Тогда первая последовательность с $n$ открывающимися скобками будет иметь вид:
+
Для того чтобы определить лексикографический порядок для правильных скобочных последовательностей надо установить порядок на алфавите, например так '$($' < '$)$'.
 
+
Для последовательностей с разным типом скобок надо определять свой порядок в зависимости от числа скобок, причем любая открывающаяся скобка должна быть меньше закрывающейся, например "$($" < "$[$" < "$)$" < "$]$".
{| border="1" cellpadding="3"
 
| (||(||(||(||...||(||(||(||)||)||)||...||)||)||)||)||(***)
 
|-
 
|0||0||0||0||...||0||0||0||1||1||1||...||1||1||1||1
 
|}
 
 
 
что соответствует самому маленькому возможному числу, а последняя:
 
 
 
{| border="1" cellpadding="3"
 
| (||)||(||)||...||(||)||(||)||(||)||...||(||)||(||)
 
|-
 
|0||1||0||1||...||0||1||0||1||0||1||...||0||1||0||1
 
|}
 
 
 
что соответствует самому большому возможному числу.
 
Для последовательностей с разным типом скобок надо определять свой порядок, например "$($"<"$[$"<"$)$"<"$]$".
 
  
 
''Примеры лексикографического порядка для $n$ и $k$, где $n$ - число открывающихся скобок, а $k$ - число видов скобок''
 
''Примеры лексикографического порядка для $n$ и $k$, где $n$ - число открывающихся скобок, а $k$ - число видов скобок''
Строка 99: Строка 74:
 
|id = def1
 
|id = def1
 
|definition =Числа Каталана {{---}} последовательность чисел, выражающих:
 
|definition =Числа Каталана {{---}} последовательность чисел, выражающих:
*количество неизоморфных упорядоченных бинарных деревьев с корнем и $n + 1$ листьями;
+
*количество не изоморфных упорядоченных бинарных деревьев с корнем и $n + 1$ листьями;
*количество способов соединения $2n$ точек на окружности n непересекающимися хордами;
+
*количество способов соединения $2n$ точек на окружности n не пересекающимися хордами;
*количество разбиений выпуклого $(n + 2)$ - угольника на треугольники непересекающимися диагоналями;
+
*количество разбиений выпуклого $(n + 2)$ - угольника на треугольники не пересекающимися диагоналями;
 
*количество правильных скобочных последовательностей имеющих $n$ открывающихся скобок.}}
 
*количество правильных скобочных последовательностей имеющих $n$ открывающихся скобок.}}
Числа Каталана удовлетворяют следующему рекурентному соотношению:
+
Числа Каталана удовлетворяют следующему рекуррентному соотношению:
  
 
$C_0 = 1$; {{---}} так как существует только одна скобочная последовательность с $0$ открывающихся скобок - пустая
 
$C_0 = 1$; {{---}} так как существует только одна скобочная последовательность с $0$ открывающихся скобок - пустая
  
 
$C_n = \sum_{i = 1}^{n - 1} C_i C_{n - 1 - i}$.
 
$C_n = \sum_{i = 1}^{n - 1} C_i C_{n - 1 - i}$.
 
+
Для этого надо перебрать все возможные последовательности $S$ и $S2$, являющиеся правильными скобочными последовательностями, такие, что $(S1)S2$ образуют новые правильные скобочные последовательности необходимой нам длины.
Это соотношение легко получается из (*). Для этого надо перебрать все возможные последовательности $d_1$ и $d_2$, являющиеся правильными скобочными последовательностями, такие, что $(d_1)d_2$ образуют новые правильные скобочные последовательности необходимой нам длины.
 
  
  
Строка 117: Строка 91:
 
''Генерация следующей скобочной последовательности:''
 
''Генерация следующей скобочной последовательности:''
  
Пусть нам известна строка $s$, представляющая собой правильную скобочную последовательность. Нам необходимо вывести следующую скобочную последовательность, а если ее нет, то - "No solution". Воспользуемся интерпретацией (**). Чтобы получить следующий битовый вектор надо найти самый последний нулевой элемент, заменить его на единицу, а элементы следующие за ним сделать минимально возможными (все нули). Тоже самое и со скобочными последовательностями, только после замены нуля на единицу оставшиеся скобки надо расположить в минимальном порядке (в виде (***)):
+
Пусть нам известна строка $s$, представляющая собой правильную скобочную последовательность. Нам необходимо вывести следующую скобочную последовательность, а если ее нет, то - "No solution". Чтобы получить следующую скобочную последовательность надо найти минимальную:
  
   function next(var s: string): boolean;
+
   bool next(s):  
    var
+
       pointer_close = 0
      i, k, l:integer;   
+
       pointer_open = 0
    begin 
+
       for (i = length(s); i > 0; i--):
       k := 0;  {счетчик для закрывающихся скобок}
+
           if (s[i] == '('):
       l := 0;  {счетчик для закрывающихся скобок}
+
             pointer_open++
       for i := length(s) downto 1 do  {Начинаем перебирать скобки с конца}
+
            if (pointer_closed > pointer_open)
        begin
+
               break
           if s[i] = '(' then
 
             begin
 
              inc(l)
 
              if k > l then  {встретив открывающуюся скобку, которую можно поменять на закрывающуюся, меняяем ее и выходим из цикла}
 
               break;
 
            end
 
 
           else  
 
           else  
           inc(k);
+
           pointer_closed++
        end;
+
       delete(s, length(s) - pointer_open - pointer_closed + 1, pointer_closed + l)
       delete(s, length(s) - l - k + 1, k + l);  {удаляем все скобки включая открывающуюся}
+
       if (s = ''):
       if s = '' then
+
        return false
      next := false
+
       s = s +')'
       else
+
      for (j = 1; j > l + 1; j++):
        begin
+
        s = s + '('
          s := s +')';  {записываем закрывающуюся скобку}
+
      for (j = 1; j > pointer_closed; j++):
          for j := 1 to l do  {расставляем скобки в минимально возможном порядке}
+
        s = s + ')'
          s := s + '(';
+
      return true
          for j := 1 to k - 1 do
 
          s := s + ')';
 
          next := true;
 
        end;
 
    end;
 
  
 
Если эта функция после выполнения выводит $true$ тогда надо напечатать полученную строку $s$, если $false$, то следует вывести "No solution".
 
Если эта функция после выполнения выводит $true$ тогда надо напечатать полученную строку $s$, если $false$, то следует вывести "No solution".
Строка 156: Строка 119:
 
Пусть нам известно число $n$. Надо вывести все правильные скобочные последовательности в лексикографическом порядке с $n$ открывающимися скобками:
 
Пусть нам известно число $n$. Надо вывести все правильные скобочные последовательности в лексикографическом порядке с $n$ открывающимися скобками:
  
   procedure (n: integer);
+
   void (n)
     var
+
     s = ' ';
      s: string;
+
    if (n == 0):
      j: integer;
+
      result(s)   
      t: boolean;
+
    else
      begin
+
      for (j = 1; j < n + 1; i++)
        s := ' '; {s присваиваем значение пустой строки}
+
        s = s + '(';
        if n = 0 then
+
      for (j = 1; j < n + 1; i++)
        writeln(s)   
+
        s = s + ')';
        else
+
      result(s);
          begin
+
      t = next(s);
            for j := 1 to n do  {создаем начальную строку}
+
      while (t <> false)
            s := s + '(';
+
        result(s);
            for j := 1 to n do
+
        t = next(s);
            s := s + ')';
+
    return
            writeln(s);
 
            t := next(s);
 
            while t <> false do  {выполняем до тех пор пока не будет получена последняя последовательность}
 
              begin
 
                writeln(s);
 
                t := next(s);
 
              end;
 
          end;
 
      end;
 
  
 
Так же с помощью этого алгоритма можно получить скобочную последовательность по номеру и номер по скобочной последовательности, добавив сравнение с нужной последовательностью и счетчик. Но это далеко не самый оптимальный алгоритм для подобного типа задач и он не будет нормально работать для больших $n$.
 
Так же с помощью этого алгоритма можно получить скобочную последовательность по номеру и номер по скобочной последовательности, добавив сравнение с нужной последовательностью и счетчик. Но это далеко не самый оптимальный алгоритм для подобного типа задач и он не будет нормально работать для больших $n$.

Версия 02:44, 15 января 2012

<wikitex>

Определения

Определение:
Скобочная последовательность — класс комбинаторных объектов, представляющий собой последовательность скобочных символов.

Примеры скобочных последовательностей:

  • $(())))($
  • $)()()))()(()())$
Определение:
Правильная скобочная последовательность — частный случай скобочной последовательности, определяющийся следующими образами:
  • "" (пустая строка) есть правильная скобочная последовательность;
  • пусть $S$ - правильная скобочная последовательность, тогда $(S)$ есть правильная скобочная последовательность;
  • пусть $S$ - правильная скобочная последовательность,тогда $()S$ и $S()$ есть правильные скобочные последовательности;

Примеры правильных скобочный последовательностей:

  • $((()()()()))$
  • $(())(()())$

Алгоритм проверки правильности скобочной последовательности

Пусть нам дана скобочная последовательность, записанная в строку $s$. Возьмем переменную $a$, $a = 0$. Будем последовательно перебирать все символы этой строки. Если мы встречаем открывающуюся скобку, то увеличиваем $a$ на $1$, закрывающую - уменьшаем на $1$. Если на протяжении всего перебора $a$ было неотрицательным и после завершения осталось нулем, то скобочная последовательность правильна.

псевдокод:

 bool check(s):
   pointer = 0
   for (i = 1; i < length(s) + 1; i++):
     pointer = (s[i] == '(')? pointer++ : pointer-- 
     if (pointer < 0)
     return false 
   if (pointer = 0)
   return true
   else
   return false

Надо отметить что, скобочные последовательности могут состоять не только из одного типа скобок, при этом недопустимо такое расположение, когда один тип скобок закрывает другой:

Примеры скобочных последовательностей с несколькими типами скобок:

  • $()[()]\{()()[]\}$ - верно
  • $[(]\{\})$ - неверно

В этом случае для проверки надо будет использовать стек.

Лексикографический порядок порядок правильных скобочных последовательностей

Для того чтобы определить лексикографический порядок для правильных скобочных последовательностей надо установить порядок на алфавите, например так '$($' < '$)$'. Для последовательностей с разным типом скобок надо определять свой порядок в зависимости от числа скобок, причем любая открывающаяся скобка должна быть меньше закрывающейся, например "$($" < "$[$" < "$)$" < "$]$".

Примеры лексикографического порядка для $n$ и $k$, где $n$ - число открывающихся скобок, а $k$ - число видов скобок

$n = 3$ $k = 1$
$((()))$ $(()())$ $(())()$ $()(())$ $()()()$
$n = 2$ $k = 2$
$()[]$ $([])$ $[()]$ $[]()$

Алгоритм генерации лексикографического порядка будет предложен ниже.

Количество правильных скобочных последовательностей. Числа Каталана

Количество правильных скобочных последовательностей со скобками одного типа совпадает с числами Каталана.

Определение:
Числа Каталана — последовательность чисел, выражающих:
  • количество не изоморфных упорядоченных бинарных деревьев с корнем и $n + 1$ листьями;
  • количество способов соединения $2n$ точек на окружности n не пересекающимися хордами;
  • количество разбиений выпуклого $(n + 2)$ - угольника на треугольники не пересекающимися диагоналями;
  • количество правильных скобочных последовательностей имеющих $n$ открывающихся скобок.

Числа Каталана удовлетворяют следующему рекуррентному соотношению:

$C_0 = 1$; — так как существует только одна скобочная последовательность с $0$ открывающихся скобок - пустая

$C_n = \sum_{i = 1}^{n - 1} C_i C_{n - 1 - i}$. Для этого надо перебрать все возможные последовательности $S$ и $S2$, являющиеся правильными скобочными последовательностями, такие, что $(S1)S2$ образуют новые правильные скобочные последовательности необходимой нам длины.


Алгоритмы генерации

Генерация следующей скобочной последовательности:

Пусть нам известна строка $s$, представляющая собой правильную скобочную последовательность. Нам необходимо вывести следующую скобочную последовательность, а если ее нет, то - "No solution". Чтобы получить следующую скобочную последовательность надо найти минимальную:

 bool next(s): 
     pointer_close = 0
     pointer_open = 0
     for (i = length(s); i > 0; i--):
         if (s[i] == '('):
           pointer_open++ 
           if (pointer_closed > pointer_open)
             break
         else 
         pointer_closed++
     delete(s, length(s) - pointer_open - pointer_closed + 1, pointer_closed + l)
     if (s = ):
       return false
     s = s +')'
     for (j = 1; j > l + 1; j++):
       s = s + '('
     for (j = 1; j > pointer_closed; j++):
       s = s + ')'
     return true

Если эта функция после выполнения выводит $true$ тогда надо напечатать полученную строку $s$, если $false$, то следует вывести "No solution".

Получение лексикографического порядка:

Пусть нам известно число $n$. Надо вывести все правильные скобочные последовательности в лексикографическом порядке с $n$ открывающимися скобками:

 void (n)
   s = ' ';
   if (n == 0):
     result(s)  
   else
     for (j = 1; j < n + 1; i++)
       s = s + '(';
     for (j = 1; j < n + 1; i++)
       s = s + ')';
     result(s);
     t = next(s);
     while (t <> false)
       result(s);
       t = next(s);
   return

Так же с помощью этого алгоритма можно получить скобочную последовательность по номеру и номер по скобочной последовательности, добавив сравнение с нужной последовательностью и счетчик. Но это далеко не самый оптимальный алгоритм для подобного типа задач и он не будет нормально работать для больших $n$.