2013-02-10 49 views
5

Tôi vừa mới bắt đầu với cụm từ thông dụng trong perl. Sau khi chơi xung quanh thông qua các hướng dẫn trực tuyến khác nhau, tôi muốn viết một biểu thức chính quy khớp với thứ tự được chỉ định theo thứ tự từ không khớp.Tìm kiếm từ được sắp xếp theo từng trường hợp thông qua cụm từ thông dụng

Tôi đang cố gắng xác định xem chuỗi "A" có bao gồm một từ hoặc một chuỗi các từ của chuỗi "B" và tôi muốn làm điều này không phân biệt chữ hoa chữ thường. Ví dụ: nếu chuỗi "B" là "John Von Neumann", thì "JOhn", "Von NeumaNn", "VoN", "john neuMann" sẽ là khớp với, nhưng các chuỗi như "Joh" , "NeumaNn VoN", "Vonn" sẽ không phải là phù hợp.

Tôi không chắc chắn cách thực hiện điều này bằng các biểu thức chính quy, bất kỳ ý tưởng nào?

+0

@ogzd được đăng '/^(? =.) (?: \ Bjohn \ b)? \ S * (?: \ Bvon \ b)? \ S * (?: \ Bneumann \ b)? \ Z/i' (với một số trợ giúp). Tôi không chắc tại sao anh ta lại xóa nó. Nó không phải là một giải pháp mục đích chung, nhưng OP có thể không cần một giải pháp mục đích chung. – ikegami

+0

@ikegami: '(? =.)' Cần phải là '(? = \ S)' nếu không mẫu sẽ khớp với một chuỗi các khoảng trắng. – Borodin

Trả lời

9

Hãy bỏ qua trường hợp trong một giây.

John Von Neumann 

có thể sánh kịp

John Von Neumann 1 1 1 
John Von   1 1 0 
John  Neumann 1 0 1 
John    1 0 0 
    Von Neumann 0 1 1 
    Von   0 1 0 
     Neumann 0 0 1 

Vì vậy, các mẫu biểu thức chính mà bạn đang tìm kiếm là

/^(?:John Von Neumann|John Von|John Newmann|John|...)\z/i 

Đây là cách bạn có thể xây dựng danh sách:

sub true_indexes { 
    my ($n) = @_; 
    my $i = 0; 
    my @indexes; 
    while ($n) { 
     push @indexes, $i if $n & 1; 
     ++$i; 
     $n >>= 1; 
    } 
    return @indexes; 
} 

my @words = split(' ', 'John Von Neumann'); 

my @patterns; 
unshift @patterns, join ' ', @words[ true_indexes($_) ] 
    for 1 .. (2**@words)-1; 

Và cuối cùng, chúng tôi có thể tạo mẫu:

my $pat = join '|', map quotemeta, @patterns; 
my $re = qr/$pat/i; 

Bạn muốn sử dụng nó như ví dụ: giải pháp

if ($input =~ /^$re\z/) { 
    print "match\n"; 
} else { 
    print "no match\n"; 
} 
+0

+1 cho 2^{n} - tổng số tập con của tập hợp. :) – gaussblurinc

+0

@kamata sử dụng tốt cú pháp Perl –

+0

Tôi nghĩ giải pháp của ogzd tốt hơn nhiều so với giải pháp này, nếu được sử dụng làm cơ sở để xây dựng regex tổng quát. Giải pháp này sẽ xây dựng một regex humongous cho cụm từ dài hơn (DFA có thể được giảm, nhưng thực tế là regex là humongous sẽ không thay đổi). Không chắc chắn lý do tại sao điều này nhận được rất nhiều upvotes. – nhahtdh

3

Ikegami của sẽ mất không gian hàm mũ cho để lưu trữ các chuỗi trước khi nó được biến thành regex (mỗi từ sẽ xuất hiện 2 n - 1 lần, trong đó n là số từ, do đó tổng không gian tối thiểu là 2 n - 1 * Tổng (độ dài của từ)). Đây là không liên quan đến công cụ regex - vì vấn đề là trước khi chuỗi được chuyển thành regex.


Một tương đương xây dựng regex (trong nhiệm kỳ của tập hợp các chuỗi rằng nó phù hợp) để giải pháp Ikegami của sẽ là:

^(?=[^ ])(?:(?: |^)John(?= |\z))?+(?:(?: |^)Von(?= |\z))?+(?:(?: |^)Neumann(?= |\z))?+\z 

này chỉ chiếm không gian tuyến tính, trong nhiệm kỳ của số từ và tổng chiều dài của tất cả các từ.

Để rõ ràng:

^ 
(?=[^ ]) 
(?:(?: |^)John(?= |\z))?+ 
(?:(?: |^)Von(?= |\z))?+ 
(?:(?: |^)Neumann(?= |\z))?+ 
\z 

Cái nhìn-phía trước khẳng định (?=[^ ]) có 2 mục đích: ngăn chặn các chuỗi rỗng không bị phù hợp, và chắc chắn rằng các ký tự đầu tiên không phải là một nhân vật không gian.

Lưu ý ?+, mà làm cho lượng hóa sở hữu (hoặc nguyên tử), vì chúng ta không cần phải quay lại đây. Tại sao? Nếu chúng ta làm điều này bình thường, chúng ta sẽ lặp qua danh sách các từ và so sánh nó với từ bên trái nhất trong đầu vào. Sau khi chúng ta tìm thấy một trận đấu, chúng ta sẽ tiếp tục vòng lặp để so sánh nó với từ tiếp theo trong đầu vào, cho đến khi tất cả các từ trong đầu vào được tìm thấy hoặc chúng ta đã hoàn thành việc lặp lại danh sách các từ.

Số sở hữu số liệu định lượng cũng ngăn chặn xảy ra lỗi địa ngục ngược. Nếu một từ được coi là trùng khớp, từ đó sẽ không bao giờ được xem xét lại.

Đối với mỗi từ, chúng có thể được đặt trước bởi dấu cách hoặc đó là phần đầu của chuỗi. Xác nhận xem trước (?= |\z) có mục đích đảm bảo các từ có cùng tiền tố không được kết hợp không đúng lúc thử đầu tiên (ví dụ: "John Von Vone", thử khớp với "John Vone").

Vì không có backtracking, hiệu suất trường hợp xấu nhất là tuyến tính về độ dài của tất cả các từ và độ dài của chuỗi đầu vào (giống như cách bạn sẽ làm điều đó mà không có regex).


Chúng có thể thay regex một chút để cho phép khoảng cách linh hoạt:

^(?= *+[^ ])(?: *+John(?= |\z))?+(?: *+Von(?= |\z))?+(?: *+Neumann(?= |\z))?+ *+\z 

Đối với độ rõ nét (không gian hàng đầu có ý nghĩa):

^ 
(?= *+[^ ]) 
(?: *+John(?= |\z))?+ 
(?: *+Von(?= |\z))?+ 
(?: *+Neumann(?= |\z))?+ 
*+ 
\z 

Cái nhìn-phía trước (?= *+[^ ]) tại bắt đầu đảm bảo chuỗi đầu vào không chỉ chứa dấu cách.

Regex được thay đổi để cho phép bất kỳ khoảng trắng nào đứng trước một từ (backtracking không được phép bởi định lượng sở hữu). 0 hoặc nhiều định lượng hơn * được sử dụng, trong trường hợp từ đó là đúng ở đầu chuỗi. Không có cơ hội cho 2 từ va chạm, do xác nhận nhìn phía trước (?= |\z).

Nó vẫn chiếm không gian tuyến tính khi xây dựng chuỗi (trước khi nhập nó vào công cụ regex). Hiệu suất trường hợp xấu nhất cũng là tuyến tính.


trường hợp cực

  1. Những lời gốc:

    aaaaaaaaaaaaaaaaaaa0 aaaaaaaaaaaaaaaaaaa1 ... aaaaaaaaaaaaaaaaaaa9 aaaaaaaaaaaaaaaaaaaa ... aaaaaaaaaaaaaaaaaaaz aaaaaaaaaaaaaaaaaaaA ... aaaaaaaaaaaaaaaaaaaZ 
    

    (Mỗi từ là 20 nhân vật, những thay đổi cuối cùng nhân vật từ 0-9, sau đó a-z, sau đó A-Z)

    Strings để tìm kiếm (không phù hợp):

    aaaaaaaaaaaaaaaaaaaz aaaaaaaaaaaaaaaaaaay 
    

    (y chỉ có thể đến trước z)

  2. Từ gốc:

    patterns used in Perl pattern matching evolved from those supplied 
    

    (Một số từ bình thường)

    Các chuỗi để tìm kiếm (không khớp):

    patterns used in Perl pattern matching evolved from those suppliedd 
    

    (tắm d vào cuối)

  3. Từ gốc:

    aaaaaaaaaaaa aaaaaaaaaaa aaaaaaaaaa aaaaaaaaa aaaaaaaa aaaaaaa aaaaaa aaaaa aaaa 
    

    (. Lời chỉ chứa a, với chiều dài khác nhau)

    Strings để tìm kiếm (không phù hợp):

    aaaaaaaaaaaa aaaaaaaaaaa aaaaaaaaaa aaaaaaaaa aaaaaaaa aaaaaaa aaaaaa aaaaa aaaaa 
    

    (Thêm a vào cuối)

  4. Từ gốc:

    performance abc xyz performance 456 [email protected]# performance 
    

    (Same từ xuất hiện nhiều lần)

    Strings để tìm kiếm (không phù hợp):

    performance performance performance performance 
    
Các vấn đề liên quan