如何更有效地计算n个字符串之间的不匹配分数?

时间:2018-05-17 15:31:33

标签: c++ algorithm performance time-complexity

假设我有一个包含n个字符串的向量,其中字符串的长度为5 ... n。必须逐个字符地比较每个字符串。如果不匹配,则分数增加1。如果匹配,则分数不会增加。然后我会将得到的分数存储在矩阵中。

我已通过以下方式实现此目的:

for (auto i = 0u; i < vector.size(); ++i)
{
  // vector.size() x vector.size() matrix
  std::string first = vector[i]; //horrible naming convention
  for (auto j = 0u; j < vector.size(); ++j)
  {
    std::string next = vector[j];
    int score = 0;
    for (auto k = 0u; k < sizeOfStrings; ++k)
    {
      if(first[k] == second[k])
      {
        score += 0;
      }
      else
      {
        score += 1;
      }
    }
    //store score into matrix
  }
}

我对此解决方案不满意,因为它是O(n^3)。所以我一直试图想出其他方法来提高效率。我已经考虑过编写另一个函数来替换我们j for循环的内部函数,但是,这仍然是O(n^3),因为函数仍然需要一个k循环。

我还考虑了一个队列,因为我只关心string[0]string[1]string[n]的比较。 String[1]string[2]string[n]进行了比较。 String[2]string[3]string[n]等进行比较。所以我的解决方案有不必要的计算,因为每个字符串都与其他字符串进行比较。这个问题,我不确定如何构建我的矩阵。

我终于找到了std模板库,但std::mismatch似乎不是我想要的,或std::find。你们还有什么其他想法?

4 个答案:

答案 0 :(得分:2)

我认为你不能轻易摆脱O(n ^ 3)比较,但你可以轻松实现你所谈论的变化。由于比较只需要以一种方式完成(即将字符串[1]与字符串[2]进行比较与将字符串[2]与字符串[1]进行比较相同),正如您所指出的,您不需要迭代每次通过整个数组,并可以将内循环的起始值更改为外循环的当前索引:

for (auto i = 0u; i < vector.size(); ++i) {
    // vector.size() x vector.size() matrix
    std::string first = vector[i]; //horrible naming convention
    for (auto j = i; j < vector.size(); ++j) {

要将其存储在矩阵中,请设置i x j矩阵,将其初始化为全零,并将每个分数存储在M[i][j]

for (auto k = 0u; k < sizeOfStrings; ++k) {
    if (first[k] != second[k]) {
        M[i][j]++;
    }
}

答案 1 :(得分:1)

如果你有n个长度为m的字符串,那么无论如何(即使你的队列想法),你必须至少做(n-1)+(n-2)+ ... +(1) = n(n-1)/ 2个字符串比较,所以你必须做(n(n-1)/ 2)* m char比较。所以无论如何,你的算法将是O(mn ^ 2)。

答案 2 :(得分:1)

  • 一般评论:

您不必相互比较相同的字符串。更重要的是,当你已经计算了那些差异时,你从第二个循环中的每次开始开始,所以更改第二个循环从i+1开始。 通过这样做,您的复杂性将会降低,因为您不会检查已经检查过的字符串或相同的字符串。

  • 改进

对向量进行排序并删除重复的条目,然后浪费计算来检查相同的字符串,只检查那些不同的字符串。

答案 3 :(得分:1)

其他答案表明这至少是O(mn ^ 2)或O(n ^ 3)是不正确的。这可以在O(mn)时间内完成,其中m是字符串大小,n是字符串数。

为简单起见,我们首先假设所有字符都是ascii。

您有一个数据结构:

int counts[m][255]

其中count [x] [y]是字符串中索引x处具有ascii字符y的字符串数。

现在,如果你没有限制为ascii,那么你需要使用std :: map

map counts[m]

但它的工作方式相同,在索引m的计数中你有一张地图,其中地图y,z中的每个条目告诉你在索引m处使用字符y的字符串数量。您还需要选择具有恒定时间查找和恒定时间插入的地图以匹配复杂性。

回到ascii和数组

int counts[m][255] // start by initializing this array to all zeros

首先初始化数据结构:

m是字符串的大小, vec是带有字符串

的std :: vector
for (int i = 0; i < vec.size(); i++) {
    std::string str = vec[i];
    for(int j = 0; j < m; j++) {
        counts[j][str[j]]++;
    }
}

现在您已拥有此结构,您可以轻松计算得分:

for (int i = 0; i < vec.size(); i++) {
    std::string str = vec[i];
    int score = 0;
    for(int j = 0; j < m; j++) {
            score += counts[j][str[j]] - 1; //subtracting 1 gives how many other strings have that same char at that index
    }
    std::cout << "string \"" << str << "\" has score " << score;
}

正如您在此代码中看到的,这是O(m * n)