Như bạn đã chỉ ra, một cây hậu tố hoặc cây gốc có lẽ là con đường để đi. Tôi muốn đề xuất:
Tạo một số radix tree, lưu id trong lá.Kiểm tra các liên kết trong this answer để bắt đầu, nhưng tôi tin rằng bạn sẽ phải tinh chỉnh bất cứ điều gì bạn tìm thấy để phù hợp với nhu cầu của bạn;
Tạo id ánh xạ dict cho đường dẫn trong cây. Điều này sẽ cho phép bạn nhanh chóng lấy câu bằng id (tìm đường dẫn, theo dõi nó để gắn kết câu). Lưu ý rằng điều này sẽ làm cho chèn và xóa một chút tốn kém: mỗi khi một nút không phải lá được thay đổi, mỗi hậu duệ sẽ cần phải có đường dẫn của họ được cập nhật trong dict;
2.1. Một thay thế (trong trường hợp các đường dẫn kết thúc quá dài) là có mỗi nút lưu trữ một tham chiếu đến cha mẹ của nó, vì vậy dict chỉ cần tham chiếu đến nút lá. Tôi tin rằng hầu hết các triển khai hiện có không làm điều đó, vì mục tiêu chính của cố gắng là để tăng tốc độ tra cứu, không phải để nén văn bản chính nó.
Tìm kiếm theo ký tự đại diện hơi phức tạp, tùy thuộc vào độ phức tạp của nhu cầu của bạn. Ví dụ được cung cấp rất đơn giản: theo các nút cho tiền tố, cho đến khi tìm thấy ký tự đại diện, sau đó trả về tất cả các con cháu. Trong trường hợp này, một trie tổng hợp có thể dễ dàng xử lý hơn cây radix chuyên biệt hơn, nhưng các yêu cầu trong không gian cao hơn một chút.
Bằng cách này, bạn cũng có thể tối ưu hóa Trie radix của bạn để mất ít không gian,
bằng cách sử dụng một số gián tiếp
bởi thực tập các chuỗi trong các nút, và thêm các nút thêm cho dài, chuỗi con chung. Ví dụ:
unique_strings = [ # Not a real array, just an hypothetical "intern table"
"The quick ",
"brown fox ",
"green frog ",
"jumps over the lazy ",
"dog.",
"fox.",
"frog.",
]
radix_trie = (0, { # The quick *
"b":(1, { # The quick brown fox *
"j":(3, { # The quick brown fox jumps over the lazy *
"d":(4,{},1), # The quick brown fox jumps over the lazy dog.
"f":(6,{},3), # The quick brown fox jumps over the lazy frog.
}),
}),
"g":(2, { # The quick green frog *
"j":(3, { # The quick green frog jumps over the lazy *
"f":(5,{},2), # The quick green frog jumps over the lazy fox.
}),
}),
})
# The nodes ("b", "j") and ("g", "j") wouldn't occur in a regular radix tree,
# since they have no siblings. Adding them, however, gives a net gain of space.
#
# "jumps over the lazy " is a common substring of
# "brown fox jumps over the lazy " and
# "green frog jumps over the lazy fox."
# which would occur naturally in a radix tree with only the 3 sentences given.
paths = {
1:("b", "j", "d"),
2:("g", "j", "f"),
3:("b", "j", "f"),
}
Tất nhiên, ví dụ của bạn dễ thiết lập, nhưng việc tìm kiếm các dữ liệu lặp lại "trong tự nhiên" sẽ phức tạp hơn một chút. (tìm các đoạn dài phổ biến trong bất kỳ chuỗi nào:
hoạt động rất tốn kém
doable, xem cập nhật) Tuy nhiên, giả sử rằng chèn/xóa là một hoạt động không thường xuyên, đó không phải là vấn đề lớn.
Lưu ý: Tôi đề xuất một cây radix thay vì một trie vì các yêu cầu về không gian trước đây nhỏ hơn nhiều.
Cập nhật: chỉ trong trường hợp bạn đang có kế hoạch giải quyết vấn đề bằng cách cho mình, đây là một gợi ý nhiều hơn để nén dữ liệu của bạn sử dụng một cây radix: theo bài viết trên Wikipedia về longest common substring, bạn có thể xây dựng một generalised suffix tree và sử dụng nó để tìm các chất nền chung của hai hoặc nhiều chuỗi (nó cũng đề cập đến nó chủ yếu được sử dụng trong tin sinh học). Tạo một cái cho các nút của cây radix của bạn (hoặc, ít nhất, những cái ở trên một kích thước nhất định), bạn có thể tìm thấy các trường hợp bạn muốn chia chúng thành các nút nhỏ hơn.
Sử dụng ví dụ của bạn, "thường xuyên" (không có con duy nhất) cây radix sẽ là:
radix_tree = ("The quick ", {
"b":("brown fox jumps over the lazy ", {
"d":("dog.",{},1),
"f":("frog.",{},3),
}),
"g":("green frog jumps over the lazy fox.", {}, 2),
})
mà rõ ràng không làm một công việc tuyệt vời tại nén văn bản của bạn. Nhưng sau khi tạo một cây hậu tố cho tập hợp các từ trong mỗi nút, nó trở nên rõ ràng rằng " jumps over the lazy "
là một ứng cử viên tốt để được interned và tái sử dụng trong hai hoặc nhiều nút (kết quả trong ví dụ tôi đã cho thấy trước đó). Không gian được lưu sẽ luôn là (string_length - (1..2)*sizeof_node) * num_nodes
(1 cho tiền tố/hậu tố, 2 cho phần còn lại), vì vậy các chuỗi ngắn không cần phải được xem xét khi thực hiện tối ưu hóa này.
Phức tạp, có, và như Adam Mihalcin lưu ý, một giải pháp Python thuần túy có thể sẽ quá tốn kém để lưu trữ một tập dữ liệu rất lớn. Nhưng trong trường hợp không có giải pháp làm sẵn trên mạng, đây là những gì tôi sẽ cố gắng đầu tiên ...
Các liên kết trong [câu trả lời này] (http://stackoverflow.com/a/4707555/520779) có thể hữu ích. – mgibsonbr
Tại loại quy mô này, bạn sẽ cần suy nghĩ cẩn thận về các yêu cầu của mình. Ví dụ: khi bạn nói "so khớp chỉ mục", bạn có nghĩa là chúng nằm trong một thứ tự cụ thể mà bạn cần giữ lại hay chỉ cần bạn nhận được chúng theo thứ tự cố định? (Trước đây là khó khăn hơn, bởi vì các câu tương tự có thể không được sắp xếp theo thứ tự.) Khi bạn nói "phù hợp với ký tự đại diện", bạn có nghĩa là bằng tiền tố hoặc với ngôi sao ở bất kỳ đâu? (Trước đây là dễ dàng hơn, bởi vì sau đó bạn có thể sử dụng một cấu trúc kiểu cây.) – katrielalex