Tôi hiện đang xem xét các vấn đề về biểu thức chính quy có thể kết thúc chạy theo thời gian mũ khi khớp với một đầu vào nhất định, ví dụ: (a*)*
và (a|a)*
có khả năng hiển thị 'catastrophic backtracking' khi được so khớp với chuỗi aaaaab - 'trong chuỗi phù hợp, thời gian cần thiết để cố gắng để phù hợp với chuỗi tăng gấp đôi. Đây chỉ là trường hợp nếu động cơ sử dụng phương pháp backtracking/NFA để cố gắng thử tất cả các nhánh có thể trong cây trước khi bị lỗi, chẳng hạn như được sử dụng trong PCRE.Regex (a?) * Không theo cấp số mũ?
Câu hỏi của tôi là tại sao không phải là (a?)*
dễ bị tấn công? Dựa trên sự hiểu biết của tôi về backtracking, những gì sẽ xảy ra trong chuỗi "aaaab" về cơ bản những gì sẽ xảy ra với (a|a)*
. Nếu chúng ta xây dựng NFA bằng cách xây dựng Thomspson NFA tiêu chuẩn, chắc chắn cho mỗi quá trình chuyển đổi epsilon xảy ra, động cơ sẽ phải tiếp tục lấy chúng và quay ngược lại theo cách tương tự như trường hợp của hai người? Ví dụ: (bỏ qua một số bước và nơi @ thay thế epsilon):
"aaaa" khớp, nhưng không thể khớp 'b', không thành công (quay lại)
"aaaa @" khớp, 'b' fail (backtrack)
"aaa @ một" trận đấu, 'b' thất bại (backtrack)
"aaa @ một @" phù hợp, 'b' thất bại (backtrack)
...
"@ một @ a @ một @ a @ "kết quả phù hợp, 'b' không thành công (quay lại)
thử tất cả các kết hợp có thể có của epsilons và a, chắc chắn dẫn đến sự bùng nổ theo cấp số nhân của các tuyến đường?
Sẽ có ý nghĩa khi loại bỏ các chuyển tiếp epsilon khỏi NFA, nhưng tôi tin rằng điều này có tác dụng loại bỏ tất cả các yếu tố không xác định khỏi mẫu (a*)*
. Điều này chắc chắn là dễ bị tổn thương, vì vậy tôi không hoàn toàn chắc chắn những gì đang xảy ra!
Cảm ơn bạn rất nhiều trước!
Chỉnh sửa: Nó đã được chỉ ra bởi Qtax rằng epsilons vẫn không thể có mặt khi NFA được truyền tải với backtracking truyền thống, nếu không (@)*
sẽ cố gắng để phù hợp mãi mãi. Vì vậy, việc triển khai NFA nào có thể dẫn đến (a*)*
và (a|a)*
là theo cấp số nhân và (a?)*
không phải như vậy? Đây là điểm mấu chốt của câu hỏi thực sự.
Động cơ PCRE và Perl regex được tối ưu hóa theo nhiều cách. Tất cả các ví dụ của bạn sẽ thất bại một cách nhanh chóng mà không có sự phản bội dữ dội, ngay cả trên các chuỗi lớn. – Qtax
Tôi nhận thức được điều này, có thể một ví dụ tốt hơn sẽ là động cơ regex của Java sau đó, mà không thất bại một cách nhanh chóng. Các phiên bản PCRE và Perl ban đầu cũng gặp phải các vấn đề tương tự, và câu hỏi của tôi có thể được áp dụng cho họ! Tuy nhiên, trên những động cơ có vẻ dễ bị tổn thương với các mẫu như '(a *) *' (tức làđộng cơ chưa được tối ưu hóa), '(a?) *' có vẻ ổn - tại sao ?! – Jarmex
Tối ưu hóa. Biểu thức chiều rộng bằng không (không có phản ứng phụ) là vô ích và nếu nó được sử dụng như bạn mô tả (cố gắng khớp 'a' rồi chuỗi rỗng cho mỗi lần lặp lại) thì regex sẽ không bao giờ thất bại, vì bạn có thể lặp lại'() * ' không thể xác định. – Qtax