动态规划之最长递增子序列 最长不重复子串 最长公共子序列

前言动态规划:与分治法相似,即通过组合子问题来求解原问题,不同的是分治法是将问题划分为互不相交的子问题,递归求解子问题,再将他们组合起来求出原问题的解。

动态规划则应用于子问题重叠的情况,通常用来求解最优化问题。这类问题可以有很多可行解,每个解都有一个值,我们希望寻找最优值的解。

通常有4个步骤来设计动态规划算法:

1.刻画一个最优解的结构特征。

2.递归地定义最优解的值。

3.计算最优解的值,通过采用自底向上的方法。

4.利用计算出的信息构造一个最优解。

【问题1】最长递增子序列问题

【问题描述】设L=是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=,其中k1

采用一个数组temp[]保存 以当前元素结尾的最长递增子序列长度,最后求出全局最优解

更新最长递增子序列的条件:a[i]>a[j]  (i>j) 且前一个递增序列长度大于等于当前递增序列长度

//动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。
    //最长递增子序列O(N^2)
    public void longestIncreasingSubsequence2(int[] a){
    	int[] temp=new int[a.length];
    	temp[0]=1;
    	int max=0;
    	for(int i=1;ia[j]&&temp[i]<=temp[j]){//找出最大的temp[j](前一个最长递增子序列长度)temp[i]<=temp[j]
    				temp[i]=temp[j]+1;//更新最长递增子序列长度
    			}
    		}
    		max=Math.max(temp[i], max);
    	}
    	System.out.println(max);
    }
【改进】考虑到在计算每个temp[i]时都要找到最大的,由于数组无序,所以每次都需要顺序查找。可以让数组有序那么就可以使用二分查找,从而算法复杂度就可以降到O(NlogN)。可以采用一个数组存储最大递增子序列的最末元素:即: B[ temp[j] ]= aj。

在数组B中用二分查找法找到满足j


//O(NlogN)解法
    public void longestIncreasingSubsequence(int[] a){
    	/*
    	 * 在计算每一个f(i)时,都要找出最大的f(j)(jLen) Len++;//更新当前最大递增子序列长度;   
        }
        System.out.println(Len);
    }
【TreeSet解法】treeSet底层是使用红黑树实现,因此可以按照值的升序进行排序。
set.ceiling(i)返回set集合中比i大的最小元素。

           public int lengthOfLIS2 (int[] nums) {
                          /*
                 * TreeSet是一个有序集合,TreeSet中的元素将按照升序排列,缺省是按照自然排序进行排列,
                 * 意味着TreeSet中的元素要实现Comparable接口。或者有一个自定义的比较器。
                 * 我们可以在构造TreeSet对象时,传递实现Comparator接口的比较器对象。
                 */
                         TreeSet set = new TreeSet<>();
		        for(int i : nums) {
		        	//Returns the least element in this set greater than or equal to the given element,
		        	//or null if there is no such element.
		            Integer ceil = set.ceiling(i);
		            if(null != ceil) {
		                set.remove(ceil);
		            }
		            set.add(i);
		        }
		        return set.size();
		    }


【问题2】最长不重复子串问题
【问题描述】Given a string, find the length of the longest substring without repeating characters.

搜索过程如下:记录上一次最长子串起始位置last,然后进行下一次搜索。比较得到最长不重复子串

动态规划之最长递增子序列 最长不重复子串 最长公共子序列_第1张图片

          public int lengthOfLongestSubstring(String s) {
		    	  if(s.length()==0||s.length()==1)
		    		return s.length();
		         char[] sArr=s.toCharArray();
		        int last=0;
		        int result=-1;
		        int[] dp=new int[sArr.length];
		        dp[0]=1;
		        for(int i=1;i=last;j--){
		        		if(sArr[i]==sArr[j]){
		        			last=j+1;//更新上一次最长子串起始位置
		        			dp[i]=i-j;//最长不重复子串
		        			break;
		        		}else if(j==last){
		        			dp[i]=dp[i-1]+1;//都不重复则更新最长不重复子串
		        			
		        		}
		        	}
		        	result=Math.max(dp[i], result);
		        }
		        return result;
		    }


【问题3】两个序列的最长公共子序列

         既然是经典的题目肯定是有优化空间的,并且解题方式是有固定流程的,这里我们采用的是矩阵实现,也就是二维数组,用来LCS的长度。

 第一步:先计算最长公共子序列的长度。

 第二步:根据长度,然后通过回溯求出最长公共子序列。

现有两个序列X={x1,x2,x3,...xi},Y={y1,y2,y3,....,yi},

设一个C[i,j]: 保存Xi与Yj的LCS的长度。

递推方程为:
        动态规划之最长递增子序列 最长不重复子串 最长公共子序列_第2张图片


 //最长公共子序列
		    public int LCS(int[] a,int[] b){
		    	int[][] temp=new int[a.length+1][b.length+1]; 
		    	int result=0;int dp=0;
		    	for(int i=1;i<=a.length;i++)
		    		temp[i][0]=0;
		    	for(int j=0;j<=b.length;j++)
		    		temp[0][j]=0;
		    	for(int k=1;k<=a.length;k++){
		    		for(int l=1;l<=b.length;l++){
		    			if(a[k-1]==a[l-1])
		    				temp[k][l]=temp[k-1][l-1]+1;
		    			else if(temp[k][l-1]<=temp[k-1][l])
		    				temp[k][l]=temp[k][l-1];
		    			else
		    				temp[k][l]=temp[k-1][l];
		    			result=Math.max(temp[k][l], result);
		    		}	
		    	}
		    	return result;
		    }
动态规划的一个重要性质特点就是解决“子问题重叠”的场景,可以有效的避免重复计算,根据上面的公式其实可以发现C[i,j]一直保存着当前(Xi,Yi)的最大子序列长度。


【总结】以上就是常见动态规划问题,关键就是把问题分解为若干子问题,找到决策条件,然后进行更新,从而得到问题的最优解。

你可能感兴趣的:(算法)