动态规划之最长公共子序列问题

最长公共子序列问题描述的就是给定两个字符串,要求出这两个字符串公共的最长的子序列,例如字符串a="bcadbca",字符串b="cddbbcda",那么这两个序列的最长子序列为"cdbca"。

而要利用动态规划的方法来求解这个问题,其大体的思路类似于利用动态规划求解0-1背包问题,一步一步的求解,最终求解出最长子序列。首先将b中的第一个字符与a中的一个、两个、、、、n个进行比较,然后b中的第二个、第三个一直到最后一个进行比较,求出最长子序列的长度,然后求解最长子序列各个元素的时候就是反过来求解每一个元素是否为公共子序列的一员(通过标记),然后最后输出子序列。

设字符串a[m]、b[n],num[i][j]表示数组a中的前i个元素和数组b中的前j个元素的最长公共子序列的元素个数。

求解的思路是开始比较两个数组的最后一个元素,如果相等,则表示该元素是两元素公共的,则num[i][j]=num[i-1][j-1]+1;如果不相等,则分别比较num[i-1][j]和num[i][j-1],选择两者中较大的一个,表示数组a中前i个元素(或者i-1)与数组b中前j-1(或者j)最长的公共子序列,即num[i][j]=max{num[i-1][j] , num[i][j-1]}。当然,在i=0或者j=0的时候num[i][j]都是为0的。所以最开始求解每个num[i][j]的值。

最后在逆向反过来求解具体的元素:从两数组的最后一个元素开始,如果两个元素相等,则该元素为子序列中的一个,标记次元素,并且两数组元素当前位置向前移动一个(即i,j各减一),如果两个不相等,则取num[i-1][j]和num[i][j-1]中较大的,如果前者较大,则i-1,比较a[i-1]和b[j],否则i不减,j-1,比较a[i]和b[j-1],以此类推。

代码如下:

import java.util.Arrays;
public class longestcomment {

	public static void main(String []args)
	{
		char []a= {'b','d','c','a','b','a','c','d'};
		char []b= {'a','b','c','b','d','a','b','d'};
		int [][]num=new int [a.length+1][b.length+1];
		int []mark=new int [a.length];
		Arrays.fill(mark, 0);
			
		for(int i=0;i<=a.length;i++)
		{
			for(int j=0;j<=b.length;j++)
			{
				if(i==0||j==0)
				{
					num[i][j]=0;
				}
				else if(a[i-1]==b[j-1])
				{
					num[i][j]=num[i-1][j-1]+1;
					
				}
				else {
					if(num[i-1][j]>num[i][j-1])
					{
						num[i][j]=num[i-1][j];
					}
					else
					{
						num[i][j]=num[i][j-1];
					}
				}
			}
		}
		System.out.println("最长公共子序列的长度为:"+num[a.length][b.length]);
		System.out.print("公共子序列的元素分别为:");		
			for(int j=b.length,i=a.length;j>0&&i>0;j--)
			{
				if(a[i-1]==b[j-1])
				{
					mark[i-1]=1;
					i--;
				}
				else
				{
					if(num[i-1][j]>num[i][j-1])
					{
						i--;
						j++;
					}
				}
			}
		for(int i=0;i<a.length;i++)
		{
			if(mark[i]==1)
			{
				System.out.print(" "+a[i]);
			}
		}
		
	}
}

结果如图:

猜你喜欢

转载自blog.csdn.net/qq_40133908/article/details/83216682