如何在大字符串上应用最长公共子序列算法?

时间:2013-09-07 13:21:21

标签: c++ lcs

如何在较大的字符串(600000个字符)上应用最长的公共子序列。在DP中有什么办法吗?我已经为更短的字符串做了这个。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;

int dp[1005][1005];
char a[1005], b[1005];

int lcs(int x,int y)
{
    if(x==strlen(a)||y==strlen(b))
        return 0;
    if(dp[x][y]!=-1)
        return dp[x][y];
    else if(a[x]==b[y])
        dp[x][y]=1+lcs(x+1,y+1);
    else
        dp[x][y]=max(lcs(x+1,y),lcs(x,y+1));
    return dp[x][y];
}

int main()
{
    while(gets(a)&&gets(b))
    {
        memset(dp,-1,sizeof(dp));
        int ret=lcs(0,0);
        printf("%d\n",ret);
    }
}

3 个答案:

答案 0 :(得分:3)

您应该看看这个讨论各种设计和实现注意事项的article。有人指出,您可以使用编辑距离(或Levenshtein距离)查看Hirschberg's algorithm找到两个字符串之间的最佳对齐方式。它可以代表您简化所需的空间量。

在底部你会发现“节省空间的LCS”因此被定义为一种混合/伪代码,其中mA的长度而n的长度是B int lcs_length(char *A, char *B) { // Allocate storage for one-dimensional arrays X and Y. for (int i = m; i >= 0; i--) { for (int j = n; j >= 0; j--) { if (A[i] == '\0' || B[j] == '\0') { X[j] = 0; } else if (A[i] == B[j]) { X[j] = 1 + Y[j+1]; } else { X[j] = max(Y[j], X[j+1]); } } // Copy contents of X into Y. Note that the "=" operator here // might not do what you expect. If Y and X are pointers then // it will assign the address and not copy the contents, so in // that case you'd do a memcpy. But they could be a custom // data type with an overridden "=" operator. Y = X; } return X[0]; }

Approx2LCS

如果您对大型字母表中的字符串LCS感兴趣here is a paper。在3.2节中找到算法{{1}}。

答案 1 :(得分:2)

首先,使用自下而上的动态编程方法:

// #includes and using namespace std;

const int SIZE = 1000;
int dp[SIZE + 1][SIZE + 1];
char a[SIZE + 1], b[SIZE + 1];

int lcs_bottomUp(){
    int strlenA = strlen(a), strlenB = strlen(b);
    for(int y = 0; y <= strlenB; y++)
        dp[strlenA][y] = 0;
    for(int x = strlenA - 1; x >= 0; x--){
        dp[x][strlenB] = 0;
        for(int y = strlenB - 1; y >= 0; y--)
            dp[x][y] = (a[x]==b[y]) ? 1 + dp[x+1][y+1] :
                    max(dp[x+1][y], dp[x][y+1]);
    }
    return dp[0][0];
}

int main(){
    while(gets(a) && gets(b)){
        printf("%d\n", lcs_bottomUp());
    }
}

请注意,您只需要保留2行(或列),一行用于dp[x],另一行用于dp[x + 1]

// #includes and using namespace std;

const int SIZE = 1000;
int dp_x[SIZE + 1]; // dp[x]
int dp_xp1[SIZE + 1]; // dp[x + 1]
char a[SIZE + 1], b[SIZE + 1];

int lcs_bottomUp_2row(){
    int strlenA = strlen(a), strlenB = strlen(b);
    for(int y = 0; y <= strlenB; y++)
        dp_x[y] = 0; // assume x == strlenA
    for(int x = strlenA - 1; x >= 0; x--){
        // x has been decreased
        memcpy(dp_xp1, dp_x, sizeof(dp_x)); // dp[x + 1] <- dp[x]

        dp_x[strlenB] = 0;
        for(int y = strlenB - 1; y >= 0 ; y--)
            dp_x[y] = (a[x]==b[y]) ? 1 + dp_xp1[y+1] :
                    max(dp_xp1[y], dp_x[y+1]);
    }
    return dp_x[0]; // assume x == 0
}

int main(){
    while(gets(a) && gets(b)){
        printf("%d\n", lcs_bottomUp_2row());
    }
}

现在可以将SIZE更改为600000

答案 2 :(得分:1)

正如OP所说,其他答案花费了太多时间,主要是因为每次外部迭代都会复制600000个字符。 为了改进它,人们可以而不是物理地改变列,从逻辑上改变它。因此:

int spaceEfficientLCS(std::string a, std::string b){
  int i, j, n = a.size(), m = b.size();

  // Size of columns is based on the size of the biggest string
  int maxLength = (n < m) ? m : n;
  int costs1[maxLength+1], costs2[maxLength+1];

  // Fill in data for costs columns
  for (i = 0; i <= maxLength; i++){
    costs1[i] = 0;
    costs2[i] = 0;
  }

  // Choose columns in a way that the return value will be costs1[0]
  int* mainCol, *secCol;
  if (n%2){
    mainCol = costs2;
    secCol = costs1;
  }
  else{
    mainCol = costs1;
    secCol = costs2;
  }

  // Compute costs
  for (i = n; i >= 0; i--){
    for (j = m; j >= 0; j--){
      if (a[i] == '\0' || b[j] == '\0') mainCol[j] = 0;
      else mainCol[j] = (a[i] == b[j]) ? secCol[j+1] + 1 :
                        std::max(secCol[j], mainCol[j+1]);
    }

    // Switch logic column
    int* aux = mainCol;
    mainCol = secCol;
    secCol = aux;
  }


  return costs1[0];
}