【问题标题】:String storage optimization字符串存储优化
【发布时间】:2014-11-12 16:33:42
【问题描述】:

我正在寻找一些 C++ 库,它可以通过在内存中仅存储一次类似(不准确)的字符串来帮助优化内存使用。它不是 FlyWeight 或字符串实习,它只能存储一次精确的对象/字符串。库应该能够分析和理解,例如,两个不同长度的特定字符串具有相同的前 100 个字符,这个子字符串应该只存储一次。
示例 1:

std::string str1 = "http://www.contoso.com/some/path/app.aspx?ch=test1"<br/>
std::string str2 = "http://www.contoso.com/some/path/app.aspx?ch=test2"<br/>

在这种情况下,很明显这两个字符串的唯一区别是最后一个字符,所以如果我们只保存一个“http://www.contoso.com/some/path/app.aspx?ch=test”的副本,然后再保存两个额外的字符串“1”,这将大大节省内存" 和 "2"
示例 2:

std::string str1 = "http://www.contoso.com/some/path/app.aspx?ch1=test1"<br/>
std::string str2 = "http://www.contoso.com/some/path/app.aspx?ch2=test2"<br/>

当有多个相同的子字符串时,情况会更加复杂:一份“http://www.contoso.com/some/path/app.aspx?ch”,然后是两个字符串“1”和“2”,一份“=test”,因为我们已经有了字符串“1 " 和 "2" 存储我们不需要任何额外的字符串。
那么,有这样的图书馆吗?有什么东西可以帮助相对快速地开发这样一个库吗?字符串是不可变的,因此无需担心更新索引或线程安全锁

【问题讨论】:

  • 这样的“子串匹配”算法可以在gzip等很多压缩程序中找到。但请记住,存储“1”通常比指向“1”的指针更有效。根据您的分块字符串实现,您可能看不到小于 16 字节的子字符串的改进。
  • 同意,但这是实现细节:)
  • 理论上可以,但实际上这意味着您只需要扫描相当大的子字符串匹配项。这产生了巨大的差异(无用匹配的指数减少)。例如,您显然没有意识到“.contoso”和“.com”包含多个相同的子字符串,即“.co”。
  • 我想当你解决最长公共子字符串问题时,可以设置一个阈值不处理短于 8 个字符的字符串
  • 比这更微妙。您最初也不处理 longer 字符串。您搜索 8 个字节的匹配项。只有当您找到潜在的匹配项时,您才会扫描以查看第一个分歧在哪里。请记住,您正在查看 O(N*N) 个潜在匹配项。

标签: c++ string optimization


【解决方案1】:
  1. 如果字符串具有公共前缀,则解决方案可能是 - 使用基数树(也称为 trie)(http://en.wikipedia.org/wiki/Radix_tree)来表示字符串。所以你只能存储指向树叶的指针。并通过长到树根得到整个字符串。

    hello world
    hello winter
    hell
    
           [2]
           /
    h-e-l-l-o-' '-w-o-r-l-d-[0]
                   \
                    i-n-t-e-r-[1]
    
  2. 这里还有一个解决方案:http://en.wikipedia.org/wiki/Rope_(data_structure)

    libstdc++ 实现:https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a00223.html

    SGI 文档:http://www.sgi.com/tech/stl/Rope.html

    但我认为您需要为rope 构建字符串才能正常工作。可能为每个新字符串与前一个字符串找到最长的公共前缀和后缀,然后将新字符串表示为前一个字符串前缀的串联,然后是 uniq 部分,然后是前一个字符串后缀。

【讨论】:

    【解决方案2】:

    例如1,我能想到的是Radix Tree,来自Trie的空间优化版本。我做了一个简单的谷歌,发现了很多 C++ 的实现。
    比如2,我也很好奇答案!

    【讨论】:

    • 见@k06a 的回答,它是上述问题的通用解决方案,它提供了构建块,但是,我真的不想开发完整的解决方案
    【解决方案3】:

    首先,请注意 std::string 不是不可变的,您必须确保这些字符串都不会被意外修改。

    这取决于字符串的模式。我建议使用哈希表(C++11 中的 std::unordered_map)。具体细节取决于您将如何访问这些字符串。

    您提供的两个字符串仅在“?ch”部分之后有所不同。如果您期望许多字符串会有很长的公共前缀,而这些前缀的大小几乎相同。您可以执行以下操作:

    假设前缀的大小是 43 个字符。让 s 成为一个字符串。然后,我们可以将 s[0-42] 视为哈希表中的键,并将字符串的其余部分视为值。

    例如,给定“http://www.contoso.com/some/path/app.aspx?ch=test1”,键将是“http://www.contoso.com/some/path/app.aspx?”而“ch=test1”就是这个值。如果键已经存在于哈希表中,您只需将值添加到与键关联的值集合中。否则,添加键/值对。

    这只是一个示例,键是什么,值是什么取决于您将如何访问这些字符串。

    此外,如果所有字符串中都包含“=test”,那么您不必将其与每个值一起存储。您可以只存储一次,然后在检索字符串时将其插入。所以给定值“ch1=test1”,将存储的只是“ch11”。这取决于字符串的模式。

    【讨论】:

      猜你喜欢
      • 2016-09-01
      • 2022-07-28
      • 2011-10-23
      • 2013-03-18
      • 2013-05-14
      • 2012-07-28
      • 2011-08-20
      • 2019-12-04
      • 2015-03-31
      相关资源
      最近更新 更多