Алгоритм упаковки текста

держу пари, кто-то уже решил это раньше, но мои поиски оказались пустыми.

Я хочу упаковать список слов в буфер, отслеживая начальную позицию и длину каждого слова. Фокус в том, что я хотел бы эффективно упаковать буфер, устраняя избыточность.

пример: кукольный Кукольный дом

эти можно упаковать в буфер просто как dollhouse, помня о том, что doll - это четыре буквы, начинающиеся с позиции 0,dollhouse это девять букв в 0, и house пять букв в 3.

то, что я придумал до сих пор:

  1. сортировка слов от самых длинных до самых коротких: (кукольный домик, домик, кукла)
  2. сканирование буфера, чтобы увидеть, если строка уже существует в качестве подстроки, если так Обратите внимание на местоположение.
  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 слов, для быстрой реконструкции