最长公共字序列

最长公共子序列,英文缩写为LCSLongest Common Subsequence)。

定义:一个序列 ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 称为已知序列的最长公共子序列。

两个字符串的最长子序列并不要求字符串连续,只要求有序,即统计两个字符串有多少个重复的字符。用动态规划的思路做。

C[i][j]记录以X[i]为结尾的字符串与Y[j]为结尾的字符串的LCS 的长度,分两组情况考虑:

1)X[i] == Y[j],则

C[i][j]的结果可以根据C的定义通过C[i-1][j-1],得到

2)X[i] /= Y[j],则

转化为C[i][j-1]C[i-1][j]的结果,

具体公式如下

 

如下是具体的实现

int LongestCommonLength(const std::string& first, 
    const std::string& last) {
  const size_t first_size = first.size();
  const size_t last_size = last.size();
  if (!first_size || !last_size ) {
    return 0;
  }
  // 用空间换时间
  size_t cost_map[first_size + 1][last_size + 1];
  memset(cost_map, 0, sizeof(cost_map));
  size_t max_len = 0;
  for (int i = 1; i <= first_size; ++i ){
    for (int j = 1;  j <= last_size ; ++j ) {
      if ((first[j-1] == last[i-1])) {
        cost_map[i][j]=cost_map[i-1][j-1]+1;
        putchar(first[j-1]);
      } else {
        cost_map[i][j] = std::max(cost_map[i][j-1],
              cost_map[i-1][j]);
      }
      if (max_len < cost_map[i][j]) {
        max_len = cost_map[i][j];
      }
      printf("%ld\t", cost_map[i][j]);
    }  // end-for
    printf("\n");
  }
return  max_len;
}

程序代码中,未来减少对j-1=0的判断,对每一行多分配一个存储单元。

遍历cost_map可以求出具体的子序列,

代码示例为

int k = max_len;
  int i = first_size;
  int j = last_size;
  while (k > 0) {
    if (cost_map[i][j] == cost_map[i-1][j]) {
      --i;
    } else if (cost_map[i][j] == cost_map[i][j-1]) {
      --j;
    } else {
      putchar(first[i-1]);
      --j, --i;
      --k;
    }
  }
  putchar('\n');

putchar的顺序为逆序,reverse一下即可。

如果不需要求公共子序列,C[i][j]其实只需要两维即可,可以看到,每次只需要两列即可,此方法在某一列很大的时候,比较好,具体实现为

  int LongestCommonLength(const std::string& first, 
      const std::string& last) {
    const size_t first_size = first.size();
    const size_t last_size = last.size();
    if (!first_size || !last_size ) {
      return 0;
    }
    size_t map_size = first_size + 1;
    size_t cost_map[map_size<<1];
    memset(cost_map, 0, sizeof(cost_map));
    size_t* prev_map = cost_map;
    size_t* next_map = &cost_map[map_size];
    size_t max_len = 0;
    int end_pos = 0;
    for (int i = 1; i <= last_size; ++i ){
      for (int j = 1;  j <= first_size ; ++j ) {
        if ((first[j-1] == last[i-1])) {
          next_map[j] = prev_map[j-1] + 1;
        } else {
          next_map[j] = std::max(next_map[j-1], prev_map[j]);
        }
        if (max_len < next_map[j]) {
          max_len = next_map[j];
          end_pos = j;
        }
        printf("%ld\t", next_map[j]);
      }  // end-for
      std::swap(prev_map, next_map); 
      // prev clear
      printf("\n");
    }  // end-for
    return  max_len;
}

 

 

 

posted @ 2013-03-18 23:29  westfly  阅读(227)  评论(0编辑  收藏  举报