動態規劃算法之:最長公共子序列 & 最長公共子串(LCS)

一、先科普下最長公共子序列 & 最長公共子串的區別: html

找兩個字符串的最長公共子串,這個子串要求在原字符串中是連續的。而最長公共子序列不要求連續 java

二、最長公共子串 算法

其實這是一個序貫決策問題,能夠用動態規劃來求解。咱們採用一個二維矩陣來記錄中間的結果。這個二維矩陣怎麼構造呢?直接舉個例子吧:"bab"和"caba"(固然咱們如今一眼就能夠看出來最長公共子串是"ba"或"ab") shell

   b  a  b 數組

c  0  0  0 app

a  0  1  0 dom

b  1  0  1 spa

a  0  1  0 .net

咱們看矩陣的斜對角線最長的那個就能找出最長公共子串。 unix

不過在二維矩陣上找最長的由1組成的斜對角線也是件麻煩費時的事,下面改進:當要在矩陣是填1時讓它等於其左上角元素加1。

   b  a  b

c  0  0  0

a  0  1  0

b  1  0  2

a  0  2  0

這樣矩陣中的最大元素就是 最長公共子串的長度。

在構造這個二維矩陣的過程當中因爲得出矩陣的某一行後其上一行就沒用了,因此實際上在程序中能夠用一維數組來代替這個矩陣。

2.1 代碼以下:

public class LCString2 {

	public static void getLCString(char[] str1, char[] str2) {
		int i, j;
		int len1, len2;
		len1 = str1.length;
		len2 = str2.length;
		int maxLen = len1 > len2 ? len1 : len2;
		int[] max = new int[maxLen];
		int[] maxIndex = new int[maxLen];
		int[] c = new int[maxLen]; // 記錄對角線上的相等值的個數

		for (i = 0; i < len2; i++) {
			for (j = len1 - 1; j >= 0; j--) {
				if (str2[i] == str1[j]) {
					if ((i == 0) || (j == 0))
						c[j] = 1;
					else
						c[j] = c[j - 1] + 1;
				} else {
					c[j] = 0;
				}

				if (c[j] > max[0]) { // 若是是大於那暫時只有一個是最長的,並且要把後面的清0;
					max[0] = c[j]; // 記錄對角線元素的最大值,以後在遍歷時用做提取子串的長度
					maxIndex[0] = j; // 記錄對角線元素最大值的位置

					for (int k = 1; k < maxLen; k++) {
						max[k] = 0;
						maxIndex[k] = 0;
					}
				} else if (c[j] == max[0]) { // 有多個是相同長度的子串
					for (int k = 1; k < maxLen; k++) {
						if (max[k] == 0) {
							max[k] = c[j];
							maxIndex[k] = j;
							break; // 在後面加一個就要退出循環了
						}

					}
				}
			}
		}

		for (j = 0; j < maxLen; j++) {
			if (max[j] > 0) {
				System.out.println("第" + (j + 1) + "個公共子串:");
				for (i = maxIndex[j] - max[j] + 1; i <= maxIndex[j]; i++)
					System.out.print(str1[i]);
				System.out.println(" ");
			}
		}
	}

	public static void main(String[] args) {

		String str1 = new String("123456abcd567");
		String str2 = new String("234dddabc45678");
		// String str1 = new String("aab12345678cde");
		// String str2 = new String("ab1234yb1234567");
		getLCString(str1.toCharArray(), str2.toCharArray());
	}
}
ref:

LCS的java算法---考慮可能有多個相同的最長公共子串

http://blog.csdn.net/rabbitbug/article/details/1740557


最大子序列、最長遞增子序列、最長公共子串、最長公共子序列、字符串編輯距離

http://www.cnblogs.com/zhangchaoyang/articles/2012070.html

2.2 其實 awk 寫起來也很容易:

echo "123456abcd567
234dddabc45678"|awk -vFS="" 'NR==1{str=$0}NR==2{N=NF;for(n=0;n++<N;){s="";for(t=n;t<=N;t++){s=s""$t;if(index(str,s)){a[n]=t-n;b[n]=s;if(m<=a[n])m=a[n]}else{t=N}}}}END{for(n=0;n++<N;)if(a[n]==m)print b[n]}'

ref:http://bbs.chinaunix.net/thread-4055834-2-1.html

2.3 perl的。。。真心沒看懂。。。

#!/usr/bin/perl
use strict;
use warnings;

my $str1 ="123456abcd567";
my $str2 = "234dddabc45678";
my $str = $str1 . "\n" . $str2;

my (@substr,@result);
$str =~ /(.+)(?=.*\n.*\1)(*PRUNE)(?{push @substr,$1})(*F)/;
@substr = sort { length($b) <=> length($a) } @substr;
@result = grep { length == length $substr[0] } @substr;
print "@result\n";
ref: http://bbs.chinaunix.net/thread-1333575-7-1.html


三、最長公共子序列

import java.util.Random;

public class LCS {

	public static void main(String[] args) {

		// 隨機生成字符串
		// String x = GetRandomStrings(substringLength1);
		// String y = GetRandomStrings(substringLength2);
		String x = "a1b2c3";
		String y = "1a1wbz2c123a1b2c123";
		// 設置字符串長度
		int substringLength1 = x.length();
		int substringLength2 = y.length(); // 具體大小可自行設置

		// 構造二維數組記錄子問題x[i]和y[i]的LCS的長度
		int[][] opt = new int[substringLength1 + 1][substringLength2 + 1];

		// 從後向前,動態規劃計算全部子問題。也可從前到後。
		for (int i = substringLength1 - 1; i >= 0; i--) {
			for (int j = substringLength2 - 1; j >= 0; j--) {
				if (x.charAt(i) == y.charAt(j))
					opt[i][j] = opt[i + 1][j + 1] + 1;// 狀態轉移方程
				else
					opt[i][j] = Math.max(opt[i + 1][j], opt[i][j + 1]);// 狀態轉移方程
			}
		}
		System.out.println("substring1:" + x);
		System.out.println("substring2:" + y);
		System.out.print("LCS:");

		int i = 0, j = 0;
		while (i < substringLength1 && j < substringLength2) {
			if (x.charAt(i) == y.charAt(j)) {
				System.out.print(x.charAt(i));
				i++;
				j++;
			} else if (opt[i + 1][j] >= opt[i][j + 1])
				i++;
			else
				j++;
		}
	}

	// 取得定長隨機字符串
	public static String GetRandomStrings(int length) {
		StringBuffer buffer = new StringBuffer("abcdefghijklmnopqrstuvwxyz");
		StringBuffer sb = new StringBuffer();
		Random r = new Random();
		int range = buffer.length();
		for (int i = 0; i < length; i++) {
			sb.append(buffer.charAt(r.nextInt(range)));
		}
		return sb.toString();
	}
}
REF:

字符串最大公共子序列以及最大公共子串問題

http://gongqi.iteye.com/blog/1517447

動態規劃算法解最長公共子序列LCS問題

http://blog.csdn.net/v_JULY_v/article/details/6110269

相關文章
相關標籤/搜索