输出所有的最长公共子序列
請先通過維基百科或其他搜索引擎了解如下知識點,如果已經掌握可直接跳過:
【參考網址:
http://en.wikipedia.org/wiki/Standard_Template_Library
http://www.geeksforgeeks.org/dynamic-programming-set-4-longest-common-subsequence/
】
1. 最長公共子序列?;
2. 最長公共子序列與最長公共子串的區別;
?Longest Common Subsequence / Longest Common Substring
3. 求解最長公共子序列的動態規劃的算法;
可用動態規劃算法求解的問題一般具有兩個特點:
1)重疊子問題;
2)最優子結構;
4. 通常用動態規劃算法先求最長公共子序列的長度,并將所有子問題的解都保存在一個二維數組中。
例如:
字符串X: abcabcaa
字符串Y: acbacba
m = |X| = 8
n = |Y| = 7
遞推表達式:
保存子問題的解(Tabulation)
| a | c | b | a | c | b | a | ||
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| a | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| b | 0 | 1 | 1 | 2 | 2 | 2 | 2 | 2 |
| c | 0 | 1 | 2 | 2 | 2 | 3 | 3 | 3 |
| a | 0 | 1 | 2 | 2 | 3 | 3 | 3 | 4 |
| b | 0 | 1 | 2 | 3 | 3 | 3 | 4 | 4 |
| c | 0 | 1 | 2 | 3 | 3 | 4 | 4 | 4 |
| a | 0 | 1 | 2 | 3 | 4 | 4 | 4 | 5 |
| a | 0 | 1 | 2 | 3 | 4 | 4 | 4 | 5 |
LCS(X,Y) = 5
輸出所有的最長公共子序列:
例子:
字串 X = “abcabcaa"?
字串 Y = "acbacba",
它們的所有最長公共子序列:
ababa abaca abcba acaba acaca acbaa acbca
1)按照O(mn)動態規劃的算法求出最長公共子序列的長度
子問題的解保存在二維數組dp里
dp[i][j] = { 字串 X 的 1~i 部分與字串 Y 的 1~j 部分的最長公共子序列的長度 }
2)構造字母表
S = { i | i in X or i in Y }
字母表S由出現在字串X和字串Y的所有字符構成的集合。
前面例子中 X 與 Y 構成的字母表S = "abc"。
3)字母表中的字符在兩個字串中最后出現的下標?
概念可參考Java中String類的lastIndexOf()方法
last1[i][j] = { 到字串 X 下標 i 為止,字母表第 j 個字符在字串 X 中最后一次出現的下標 }
last2[i][j] = { 到字串 Y 下標 i 為止,字母表第 j 個字符在字串 Y 中最后一次出現的下標 }
4)枚舉最長公共字串的每一個字符
從兩個字串的長度 len1 和 len2 開始枚舉字母表 S 中的每一個字符
例如,
?t1 = last1[len1][0]?
t2 = last2[len2][0]
表示字母表第0個字符在 X 字符串1---len1的最大下標為t1,?
在Y 字符串1--len2的最大下標為t2。
若dp[t1][t2]?的值為s1和s2的最大公共子序列長度 cnt , 則表示這個字符即字母表的第0個字符是最長公共子序列的最后一個字符,
并且繼續在 t1 - 1?和?t2 - 1?的子問題中尋找符合最大公共子序列長度為為cnt - 1的字符串,依此類推,直到達最長公共子序列為0時結束。
把保存的字符串放入set集合里面,讓它按字典序排序;否則繼續枚舉字母表中的下一個字符。
為什么從最長公共子序列的最后一個字符開始尋找呢?
例如:
”CDCD"?
"FUCKC"?
它們構成的字母表S = ”UDFCK"
現在枚舉字符'C',它在字母表S中的下標 = 3
last1[0][3] = 0?//下標從0開始
last1[3[3] = 2
last2[2][3] = 2
last2[4][3] = 4
根據’C'在兩個字串出現的位置,共有(0, 2)、(0, 4)、?(2, 2)?、(2, 4) 四種可能。
我們舍棄前三個,可以為后續的枚舉提供更大的空間。
5)Java語言實現
import java.util.*;/*** The string "abcabcaa" and "acbacba" have longest common subsequences:* ababa* abaca* abcba* acaba* acaca* acbaa* acbca* @author York**/public class LongestCommonSubsequence {String source;String target;int[][] c;int[][] last1;int[][] last2;int lcsLength;char[] tmp;String alphabet;HashSet<String> set = new HashSet<String>();public LongestCommonSubsequence() {}public LongestCommonSubsequence(String s, String t) {this.source = s;this.target = t;}public int LCSLength() {int m = source.length();int n = target.length();c = new int[m+1][n+1];c[0][0] = 0;for(int i = 1; i <= m; ++i) {c[i][0] = 0;}for(int j = 1; j <= n; ++j) {c[0][j] = 0;}for(int i = 1; i <= m; i++) {for(int j = 1; j <= n; j++) {if(source.charAt(i-1) == target.charAt(j-1)) {c[i][j] = c[i-1][j-1] + 1;} else if(c[i-1][j] >= c[i][j-1]){c[i][j] = c[i-1][j];} else {c[i][j] = c[i][j-1];}}}return lcsLength = c[m][n];}private void lastIndexOf(String s, int[][] last) {for (int j = 0; j < alphabet.length(); ++j) {char c = alphabet.charAt(j);for (int i = 1; i <= s.length(); ++i) {int lastIndex;for (lastIndex = i; lastIndex >= 1; --lastIndex) {if(c == s.charAt(lastIndex-1)) {last[i][j] = lastIndex;break;}}if(lastIndex <= 0) {last[i][j] = 0;}}}}private void backTraceAll(int index1, int index2, int len) {if(len <= 0) {String str = new String(tmp);System.out.println("lcs: " + str);set.add(str);return;}if(index1 > 0 && index2 > 0) {for(int j = 0; j < alphabet.length(); ++j) {//字母表中第j個字符最后一次出現在子串source(0, index1)的下標int t1 = last1[index1][j];int t2 = last2[index2][j];//數學證明是?或者給出遞歸表達式吧if(c[t1][t2] == len) {tmp[len-1] = alphabet.charAt(j);backTraceAll(t1-1, t2-1, len-1);}}}}public void printAll() {int m = source.length();int n = target.length();//由兩個字符串構成的字母表,包含它們中出現過的所有字符的集合//alphabet = {i| i in source or target};HashSet<Character> alpha = new HashSet<Character>();for(int i = 0; i < m; ++i) {alpha.add(source.charAt(i));}for(int j = 0; j < n; ++j) {alpha.add(target.charAt(j));}int length = alpha.size();Character[] characters = new Character[length];alpha.toArray(characters);char[] chars = new char[length];for(int i = 0; i < length; ++i) {chars[i] = characters[i];}alphabet = new String(chars);System.out.println("alphabet = " + alphabet);last1 = new int[m+1][alphabet.length()];last2 = new int[n+1][alphabet.length()];System.out.println("c[][]: ");for(int i = 0; i <= m; ++i) {for(int j = 0; j <= n; ++j) {System.out.print(c[i][j] + "\t");}System.out.println();}lastIndexOf(source, last1);lastIndexOf(target, last2);System.out.println("last1[][]: ");for(int i = 0; i <= m; ++i) {for(int j = 0; j < alphabet.length(); ++j) {System.out.print(last1[i][j] + " ");}System.out.println();}System.out.println("last2[][]: ");for(int i = 0; i <= n; ++i) {for(int j = 0; j < alphabet.length(); ++j) {System.out.print(last2[i][j] + " ");}System.out.println();}System.out.println("lcs length = " + lcsLength);tmp = new char[lcsLength];backTraceAll(m, n, lcsLength);System.out.println("All longest common sequences: ");for(String s: set) {System.out.println(s);}}public static void main(String[] args) {Scanner sc = new Scanner(System.in);System.out.println("Enter two strings:");String str1 = sc.nextLine();String str2 = sc.nextLine();LongestCommonSubsequence lcs = new LongestCommonSubsequence(str1, str2);int max = lcs.LCSLength();System.out.println("longest length = " + max);lcs.printAll();} }
http://blog.csdn.net/tsaid/article/details/6726698
http://blog.csdn.net/xiaoxiaoluo/article/details/8169533
http://www.cppblog.com/varg-vikernes/archive/2010/09/27/127866.html
總結
以上是生活随笔為你收集整理的输出所有的最长公共子序列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32F413 SPI+DMA接收错
- 下一篇: 如何查看谷歌账户的实际消费金额和扣款金额