最长公共字序列
最长公共子序列,英文缩写为LCS(Longest Common Subsequence)。
定义:一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。
两个字符串的最长子序列并不要求字符串连续,只要求有序,即统计两个字符串有多少个重复的字符。用动态规划的思路做。
设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; }