1. 程式人生 > >最長公共子序列(JAVA)

最長公共子序列(JAVA)

最長公共子序列問題:若給定序列X={x1,x2,…,xm},則另一序列Z={z1,z2,…,zk},是X的子序列是指存在一個嚴格遞增下標序列{i1,i2,…,ik}使得對於所有j=1,2,…,k有:zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相應的遞增下標序列為{2,3,5,7}。

給定2個序列X和Y,當另一序列Z既是X的子序列又是Y的子序列時,稱Z是序列X和Y的公共子序列。

總結起來就是:1.最長公共子序列中的元素是要倆個序列共有的。

                         2.這些元素在原序列的下標必須的遞增的。

用動態規劃的思路去求解會節約許多時間,可以先求出最長公共子序列的長度,再去求出對應的值。

void LCSLength (char x[] ,char y[], int m, int n, int c[][], int b[][])
	{
	       int i,j;
	       for (i = 1; i <= m; i++) c[i][0] = 0;//第0列設為0
	       for (i = 1; i <= n; i++) c[0][i] = 0;//第0行設為0
	       for (i = 1; i <= m; i++)
	          for (j = 1; j<= n; j++)
	          {
	            if (x[i]==y[j])
	            {
	                 c[i][j]=c[i-1][j-1]+1;
	                 b[i][j]=1;
	            }
	            else if (c[i-1][j]>=c[i][j-1])
	            {
	                 c[i][j]=c[i-1][j];
	                 b[i][j]=2;
	            }
	            else
	            {    c[i][j]=c[i][j-1];
	                 b[i][j]=3;
	            }
	         }
	}

     這個方法是尋找子序列的最大長度。3個判斷條件的結果,簡單歸納為
     [i-1,j-1] 【i-1,j】
     【i,j-1】    (i,j)
     假如,Xi=Yj,則是左上角b[i][j]=[i-1,j-1]+1。
     如果,倆者不等,假如【i-1,j】大於【i,j-1】,則是取【上】一個值b[i][j]=【i-1,j】。
     假如【i-1,j】小於【i,j-1】,則是取【左】邊一個值b[i][j]=【i,j-1】。
     即使倆個【】誰大,b[i][j]就等於誰的值。
     當倆個序列中有第一個值相等時,就將該座標記為1,並將這個“1”在該行傳遞下去,直到結束該行。
     在下一行中也將把上一行的“1”繼承,如果在此行中有第二個值相同時,就執行b[i][j]=[i-1,j-1]+1,
    就是將本來的“1”+1,變成了2,此時的最大長度就變成了2,以此類推,直至倆個序列都找完,最後的c[i][j]就是所需要的長度。

void LCS(int i,int j, char x[] ,int b[][])
	{
	      if (i ==0 || j==0) return;
	      if (b[i][j]== 1)        //輸出該序號
	      {
	           LCS(i-1,j-1,x,b);//【1】
	           System.out.print(x[i]+" ");
	      }
	      else if (b[i][j]== 2)    //向上找
	           LCS(i-1,j,x,b);
	      else                     //b[i][j]=3,向左找
	    	  LCS(i,j-1,x,b);
	}

 該方法是輸出子序列,是從大到小尋找。但不會倒序輸出,因為當滿足第一個if條件時,此時已經找到最後一個元素,
     但首先會執行【1】,即已經去尋找前一個元素,以此類推,直到找到子序列的第一個元素,接著從頭到尾輸出,完成子序列。

原始碼:

import java.util.*;
public class S3_1 {
	
	static char x[]=new char[100];
	static char y[]=new char[100];
	static int c[][]=new int[100][100];
	static int b[][]=new int[100][100];
	int t=0;
	public static void main(String[] args) {
		S3_1 s=new S3_1();
		Scanner sc=new Scanner(System.in);
		System.out.println("輸入倆個序列");
		System.out.println("輸入序列1、2的長度:");
		int m=sc.nextInt();
		int n=sc.nextInt();
		System.out.println("1:");
		for(int i=1;i<=m;i++) {
			x[i]=sc.next().charAt(0);
		}
		System.out.println("2:");
		for(int i=1;i<=n;i++) {
			y[i]=sc.next().charAt(0);
		}
		s.LCSLength(x, y, m, n, c, b);
		System.out.println("最長公共子序列為:");
		s.LCS(m, n, x, b);	
	}
	void LCSLength (char x[] ,char y[], int m, int n, int c[][], int b[][])
	{
	       int i,j;
	       for (i = 1; i <= m; i++) c[i][0] = 0;//第0列設為0
	       for (i = 1; i <= n; i++) c[0][i] = 0;//第0行設為0
	       for (i = 1; i <= m; i++)
	          for (j = 1; j<= n; j++)
	          {
	            if (x[i]==y[j])
	            {
	                 c[i][j]=c[i-1][j-1]+1;
	                 b[i][j]=1;
	            }
	            else if (c[i-1][j]>=c[i][j-1])
	            {
	                 c[i][j]=c[i-1][j];
	                 b[i][j]=2;
	            }
	            else
	            {    c[i][j]=c[i][j-1];
	                 b[i][j]=3;
	            }
	         }
	}

	void LCS(int i,int j, char x[] ,int b[][])
	{
	      if (i ==0 || j==0) return;
	      if (b[i][j]== 1)        //輸出該序號
	      {
	           LCS(i-1,j-1,x,b);//【1】
	           System.out.print(x[i]+" ");
	      }
	      else if (b[i][j]== 2)    //向上找
	           LCS(i-1,j,x,b);
	      else                     //b[i][j]=3,向左找
	    	  LCS(i,j-1,x,b);
	}
	
}