【问题标题】:Trying to store Reverse string pattern into key value pair not working (Burrows Wheeler Rotation)尝试将反向字符串模式存储到键值对中不起作用(Burrows Wheeler Rotation)
【发布时间】:2021-03-28 00:34:05
【问题描述】:

所以我试图将此索引(int)和数据(字符串)实现为一个 Dictionary 类,该类采用上述类型的索引和数据。这是我的代码:

for (int i = 0; i < size; i++){
     dict[i].setIndex(i);
     for (int j = i; j <= size; j++){
          dict[i].data += input[j];
     }
}

此代码适用于

编辑:这里的大小变量是指 input.length()。

【问题讨论】:

  • A minimal reproducible example 而不是你的 sn-p 可能更有帮助。然而,一个蓝色的镜头:外循环迭代直到size - 1for (int i = 0; i &lt; size; i++),但内循环直到sizefor (int j = i; j &lt;= size; j++){。在不知道其余部分的情况下,这可能是越界访问(或完全正确),但至少在我看来是可疑的。
  • 没有办法让这个字典明显更快,也没有办法让它更节省空间,但是,要实现 Burrows-Wheeler Rotation,你不需要这个字典来对字符串的旋转进行排序(我假设你正在尝试这样做)。如果您打算对字符串的旋转进行排序,这是一个完全不同的问题。
  • @Svistunov 我正在使用 Dictionary 类中声明的堆排序对 Dictionary 进行排序。它工作正常。但问题在于这个特定的循环。

标签: c++ algorithm optimization burrows-wheeler-transform


【解决方案1】:

因为您要对后缀进行排序,所以应该使用后缀数组,该数组旨在有效地解决此问题。它不是存储后缀本身,而是存储后缀开始的索引。每当您尝试自己存储所有足够的内容时,您都会使用O(n^2),这使得此类代码无法在更大的输入上运行。

它对这些索引进行了如下排序。它不是对后缀本身进行排序,而是对字符串的循环旋转进行排序。让我们扩展单词的子字符串含义以使用循环字符串,允许我们在结束位置之后使用起始位置。请注意,任何大小为2k 的子字符串都可以表示为两个大小为k 的子字符串的串联。因此,在我们已经对大小为k 的所有子字符串进行排序的假设下,我们可以对大小为2k 的子字符串进行排序,只需对子字符串的每一半进行两次比较即可。因此,如果使用基于比较的排序,则可以在O(n log n) 时间内将处理的子字符串长度加倍,或者在这种情况下,甚至在O(n) 中使用例如计数排序。对长度为 1 的子字符串进行排序很简单。

因此,最终算法将是:对所有大小为 1 的子字符串进行排序。然后,直到您对足够长的字符串进行排序,将排序的子字符串的大小加倍。这种加倍只能重复O(log n) 次,这意味着整个算法运行在O(n log n) 时间并使用O(n) 空间。你最终会得到一个 suffices 起始位置的索引数组,按它们所代表的后缀排序。通过这种表示,您可以轻松获取旋转字符串的最后一个字符 (ans[(suffixIndex + n - 1) % n]),或字符串的任何其他部分。

This page 有更多关于该算法的详细信息,并提供了 C++ 语言的实现

【讨论】:

  • 这个循环在我上面提到的排序后缀数组之前运行。此循环仅用于获取数组。
  • @MuhammadIqbal 你不应该获取数组本身,它总是太慢,它使用 size * size 内存,所以如果你有 10kB 的输入数据,你的字典会重 100MB,并且 1MB 的文本将导致存储字典所需的 TB。您的方法无法解决它,它只是文本本身的大小。这不仅空间效率低,而且速度慢。使用所提出的算法,您只需要 size * log(size) 时间,因此对于兆字节的输入,它会快 5-6 个数量级,而且它也是一种线性空间算法。
  • 您的算法是否有代码可以帮助我进一步理解这一点?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-20
  • 1970-01-01
  • 2023-03-03
  • 2011-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多