算法思想
本程序采用算法导论224-225页(不同版本所在书页可能不一样)的自底向上的动态规划算法。
算法思想为将一个问题(求解两个序列的最长公共子序列)分解为多个子问题(求解两个序列的最长公共子序列的最长公共子序列),再将子问题分解为子问题的子问题,直至无法分解。
由于该问题具有重叠子问题性质,可以采用带备忘录的递归算法也可以采用自底向下的动态规划算法。本程序采用后者。
核心程序
private static void match() {
m = X.length();
n = Y.length();
b = new char[m + 1][n + 1];
c = new int[m + 1][n + 1];
for (int i = 1; i <= m; i++) {
c[i][0] = 0;
b[i][0] = '0';
}
for (int j = 0; j <= n; j++) {
c[0][j] = 0;
b[0][j] = '0';
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (X.charAt(i - 1) == Y.charAt(j - 1)) {
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = '↖';
} else if (c[i - 1][j] >= c[i][j - 1]) {
c[i][j] = c[i - 1][j];
b[i][j] = '↑';
} else {
c[i][j] = c[i][j - 1];
b[i][j] = '←';
}
}
}
}
private static void getLCS(int i, int j) {
resulti[count] = i;
resultj[count] = j;
lcs[count] = b[i][j];
count++;
if (i == 0 || j == 0) {
return;
}
if (b[i][j] == '↖') {
getLCS(i - 1, j - 1);
} else if (b[i][j] == '↑') {
getLCS(i - 1, j);
} else {
getLCS(i, j - 1);
}
}
此段代码为算法导论书上224页伪代码的java实现,思想一致,代码类似,故不在此赘述。
private static void showProcess() {
Thread thread = new Thread(new Runnable() {
public void run() {
again.setEnabled(false);
String s = "";
for (int i = 0; i < count; i++) {
blabel[resulti[i] * (n + 1) + resultj[i]]
.setBackground(Color.GRAY);
blabel[resulti[i] * (n + 1) + resultj[i]].setOpaque(true);// 设置不透明
if (lcs[i] == '↖') {
s += String.valueOf(X.charAt(resulti[i] - 1));
LCSlabel.setText(s);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
again.setEnabled(true);
}
});
thread.start();
}
此段代码为动态展示构造最优解的实现。主要通过线程的休眠定时改变标签的颜色和设置标签的文本造成动态的效果。线程运行时应避免重复调用该函数导致多个线程被创建并运行而造成界面错乱,因此线程要在运行时将两个按下后会调用该函数的按钮设为不可用。线程运行结束时再将按钮设为可用。
运行结果
程序运行弹出以上窗口。
两个可输入文本框中的字母序列默认为算法导论书上的两个序列,可自行删除并输入新的序列。
点击Exit退出程序。
点击Match进行匹配。
弹出以下窗口。
以上窗口分为三个区域,左右两个区域分别展示c,b两个表,中间为控制区域,点击Start
按钮动态展示构造最优解的过程。
以下为点击后的效果:
点击Again则再次展示过程。