Як стверджують інші відповіді, пошукові запити не додають жодної додаткової сили регулярним виразам.
Я думаю, ми можемо показати це, використовуючи наступне:
Один Pebble 2-NFA (див. Розділ «Вступ», який посилається на нього).
1-гальковий 2NFA не стосується вкладених головок підстановки, але ми можемо використовувати варіант багатогалькових 2NFA (див. Розділ нижче).
Вступ
2-NFA - це недетермінований кінцевий автомат, який має можливість рухатись ліворуч або праворуч на його вході.
Машина з одним камінчиком - це місце, де машина може помістити камінчик на вхідну стрічку (тобто позначити певний камінний символ вхідним каменем) і, можливо, робити різні переходи залежно від того, чи є камінчик у поточній вхідній позиції чи ні.
Відомо, що One Pebble 2-NFA має таку ж потужність, як звичайний DFA.
Невкладені голови Lookaheads
Основна ідея така:
2NFA дозволяє нам відстежувати (або "передню доріжку"), рухаючись вперед або назад у вхідній стрічці. Отже, для lookahead ми можемо виконати збіг для регулярного виразу lookahead, а потім повернути назад те, що ми спожили, для відповідності виразу lookahead. Для того, щоб точно знати, коли зупинити зворотне відстеження, ми використовуємо гальку! Ми опускаємо камінчик, перш ніж увійти в dfa для голови, щоб позначити місце, де зворотне відстеження має зупинитися.
Таким чином, наприкінці проходження нашого рядка через гальку 2NFA ми знаємо, чи відповідали ми виразу lookahead чи ні, а вхідний лівий (тобто те, що залишилось спожити) - це саме те, що потрібно, щоб відповідати решті.
Отже, для вигляду виду u (? = V) w
У нас є DFA для u, v і w.
З приймаючого стану (так, можна припустити, що існує лише один) DFA для u, ми робимо електронний перехід у початковий стан v, позначаючи вхід камінчиком.
З приймаючого стану для v ми здійснюємо електронний перехід до стану, який продовжує рухати вхід вліво, поки не знайде камінчик, а потім переходить у початковий стан w.
Від відхиляючого стану v ми здійснюємо електронний перехід до стану, який рухається ліворуч, поки не знайде камінчик, і переходить у прийнятний стан u (тобто там, де ми зупинилися).
Доказ, який використовується для звичайних NFA, щоб показати r1 | r2, або r * і т. д., перенесіть для цього одного гальки 2nfas. Див http://www.coli.uni-saarland.de/projects/milca/courses/coal/html/node41.html#regularlanguages.sec.regexptofsa для отримання додаткової інформації про те , компонентні машини ставляться разом , щоб дати велику машину для виразу r * тощо
Причина, по якій вищезазначені докази для r * etc працюють, полягає в тому, що зворотне відстеження гарантує, що вказівник введення завжди знаходиться в потрібному місці, коли ми вводимо компонент nfas для повторення. Крім того, якщо використовується галька, вона обробляється однією з машин для пошуку деталей. Оскільки немає переходів від машини lookahead до машини lookahead без повного зворотного відстеження та повернення гальки, потрібна лише одна галька.
Наприклад, розглянемо ([^ a] | a (? = ... b)) *
і рядок abbb.
У нас є abbb, який проходить через peb2nfa для a (? = ... b), в кінці якого ми знаходимось у стані: (bbb, збіг) (тобто вхідний bbb залишається, і він відповідає "a" далі - "..b"). Тепер через * ми повертаємося до початку (див. Конструкцію за посиланням вище) і вводимо dfa для [^ a]. Зіставте b, поверніться до початку, двічі введіть [^ a], а потім прийміть.
Робота з вкладеними лукахедами
Для обробки вкладених головок пошуку ми можемо використовувати обмежену версію k-pebble 2NFA, як визначено тут: Результати складності для двосторонніх та багатогалькових автоматів та їх логіки (див. Визначення 4.1 та Теорему 4.2).
Загалом, 2 камінчики можуть приймати нерегулярні набори, але з наступними обмеженнями, k-камінчики можуть бути показані як регулярні (теорема 4.2 у цій статті).
Якщо галька P_1, P_2, ..., P_K
P_ {i + 1} не можна розміщувати, якщо P_i вже на стрічці, а P_ {i} не може бути піднятий, якщо P_ {i + 1} немає на стрічці. В основному камінчики потрібно використовувати LIFO.
Між часом, коли P_ {i + 1} розміщується, і часом, коли P_ {i} піднімається або P_ {i + 2} розміщується, автомат може обходити лише підслово, розташоване між поточним місцезнаходженням P_ {i} і кінець вхідного слова, який лежить у напрямку P_ {i + 1}. Більше того, у цьому підслові автомат може діяти лише як 1-гальковий автомат з Pebble P_ {i + 1}. Зокрема, забороняється піднімати, розміщувати або навіть відчувати присутність іншого гальки.
Отже, якщо v - вкладений вираз Lookahead глибини k, то (? = V) - вкладений вираз Lookahead глибини k + 1. Коли ми потрапляємо в машину для пошуку, ми точно знаємо, скільки гальки потрібно було покласти до цього часу, і тому можемо точно визначити, яку гальку розмістити, і коли ми виходимо з цієї машини, ми знаємо, яку гальку підняти. Усі машини на глибині t вводяться, розміщуючи гальку t, і виходять (тобто ми повертаємось до обробки машини глибини t-1), видаляючи гальку t. Будь-який запуск цілої машини виглядає як рекурсивний виклик dfs дерева, і на ці два обмеження багатогальцевої машини можна вжити.
Тепер, коли ви комбінуєте вирази, для rr1, оскільки ви конкатуєте, кількість гальки r1 повинна збільшуватися на глибину r. Для r * і r | r1 нумерація гальки залишається незмінною.
Таким чином, будь-який вираз із пошуковими головами можна перетворити на еквівалентну багатогалькову машину із зазначеними вище обмеженнями щодо розміщення гальки, і тому є регулярним.
Висновок
Це в основному усуває недолік оригінального доказу Френсіса: можливість запобігти тому, щоб вирази lookahead споживали все, що потрібно для майбутніх матчів.
Оскільки Lookbehinds - це просто скінченний рядок (насправді не регулярні вирази), ми можемо мати справу з ними спочатку, а потім мати справу з пошуковими головами.
Вибачте за неповний запис, але повний доказ передбачає малювання великої кількості фігур.
Мені це виглядає правильно, але я буду радий дізнатися про будь-які помилки (які, здається, мені подобаються :-)).