Алгоритм упаковки текста
держу пари, кто-то уже решил это раньше, но мои поиски оказались пустыми.
Я хочу упаковать список слов в буфер, отслеживая начальную позицию и длину каждого слова. Фокус в том, что я хотел бы эффективно упаковать буфер, устраняя избыточность.
пример: кукольный Кукольный дом
эти можно упаковать в буфер просто как dollhouse
, помня о том, что doll
- это четыре буквы, начинающиеся с позиции 0,dollhouse
это девять букв в 0, и house
пять букв в 3.
то, что я придумал до сих пор:
- сортировка слов от самых длинных до самых коротких: (кукольный домик, домик, кукла)
- сканирование буфера, чтобы увидеть, если строка уже существует в качестве подстроки, если так Обратите внимание на местоположение.
- если он еще не существует, добавьте его в конец буфера.
поскольку длинные слова часто содержат более короткие слова, это работает довольно хорошо, но это можно сделать значительно лучше. Например, если я расширяю список слов, чтобы включить ragdoll, то мой алгоритм придумывает dollhouseragdoll
что менее эффективно, чем ragdollhouse
.
это шаг предварительной обработки, поэтому я не очень беспокоюсь о скорости. O (n^2) в порядке. С другой стороны, мой фактический список содержит десятки тысяч слов, поэтому O(n!) это, наверное, вопрос.
в качестве примечания эта схема хранения используется для данных в таблице "имя" шрифт TrueType, cf. http://www.microsoft.com/typography/otspec/name.htm
8 ответов
Это самая короткая задача суперструны: найдите самую короткую строку, содержащую набор заданных строк в качестве подстрок. Согласно эта бумага IEEE (к которому у вас может не быть доступа, к сожалению), решение этой проблемы именно NP-complete. Однако существуют эвристические решения.
в качестве первого шага вы должны найти все строки, которые являются подстроками других строк и удалить их (конечно, вам все равно нужно записать их позиции относительно содержащихся строк каким-то образом). Эти полностью содержащиеся строки можно найти эффективно, используя обобщенное суффиксное дерево.
затем, многократно объединяя две строки, имеющие самое длинное перекрытие, вы гарантированно получите решение, длина которого не хуже, чем в 4 раза меньше минимально возможной длины. Должно быть возможно быстро найти размеры перекрытия, используя два дерева radix, как предложено комментарием Zifre on Конрад Ответ Рудольфа!--4-->. Или вы можете каким-то образом использовать обобщенное дерево суффиксов.
извините, я не могу найти для вас достойную ссылку-кажется, нет страницы Википедии или какой-либо общедоступной информации по этой конкретной проблеме. Вкратце упоминается здесь, хотя никаких предлагаемых решений не предусмотрено.
Я думаю, вы можете использовать Дерево Radix. Это стоит некоторой памяти из-за указателей на листья и родителей, но легко сопоставить строки (O(k) (где k-самый длинный размер строки).
моя первая мысль здесь: используйте структуру данных для определения общих префиксов и суффиксов ваших строк. Затем отсортируйте слова, рассматриваемые этими префиксами и постфиксами. Это приведет к желаемому ragdollhouse
.
похож на проблема с рюкзаком, который является NP-полным, поэтому нет" окончательного " алгоритма.
Я сделал лабораторию в колледже, где мы поставили задачу реализовать простую программу сжатия.
то, что мы сделали, было последовательным применением этих методов к тексту:
- BWT (преобразование Берроуза-Уилера): помогает упорядочить Буквы в последовательности одинаковых букв (подсказка* есть математические замены для получения букв вместо того, чтобы фактически делать вращения)
- MTF (перейти к переднему преобразованию): переписывает последовательность букв как последовательность индексов динамического списка.
- кодирование Хаффмана: форма кодирования энтропии, которая создает таблицу кода переменной длины, в которой более короткие коды даются часто встречающимся символам, а более длинные коды даются редко встречающимся символам
вот, я нашел страница задание.
чтобы вернуть исходный текст, вы делаете (1) декодирование Хаффмана, (2) обратный MTF и тогда (3) обратный BWT. Есть несколько хороших ресурсов по всему этому на Interwebs.
уточните Шаг 3.
- просмотрите текущий список и посмотрите, начинается ли какое-либо слово в списке с суффикса текущего слова. (Возможно, вы захотите сохранить суффикс длиннее некоторой длины-длиннее 1, например).
- Если да, то добавьте отдельный префикс к этому слову в качестве префикса к существующему слову и отрегулируйте все существующие ссылки соответствующим образом (медленно!)
- Если нет, добавьте слово в конец списка, как на текущем шаге 3.
Это даст вам "ragdollhouse" в качестве сохраненных данных в вашем примере. Неясно, всегда ли он будет работать оптимально (если у вас также есть "barbiedoll" и "доллар" в списке слов, например).
Я бы не стал изобретать колесо в очередной раз. Там уже пошло огромное количество рабочей силы в алгоритмы сжатия, почему бы не взять один из уже доступных?
вот несколько хороших вариантов:
- С помощью gzip для быстрой скорости сжатия / декомпрессии
- командой bzip2 для немного горького сжатия, но гораздо медленнее декомпрессии
- LZMA для очень высокого коэффициента сжатия и быстрая декомпрессия (быстрее, чем bzip2, но медленнее, чем gzip)
- lzop для очень быстрого сжатия / декомпрессии
Если вы используете Java, gzip уже интегрирован.
непонятно, что вы хотите сделать.
вы хотите структуру данных, которая позволяет хранить в памяти сознательным образом строки, позволяя операции, как поиск возможно в разумное количество времени?
вы просто хотите массив слов, сжимаются?
в первом случае вы можете пойти на Patricia trie или String B-Tree.
для второго случая, вы можете как раз принять некоторое techinique обжатия индекса, как что:
Если у вас есть что-то вроде:
aaa
aaab
aasd
abaco
abad
вы можете сжать так:
0aaa
3b
2sd
1baco
2ad
число-длину наибольшего общего префикса с предыдущей строкой. Вы можете настроить эту схему, например. планирование "перезапуска" общего префикса после всего K слов, для быстрой реконструкции