JAVA程序设计:最长递增子序列的个数(LeetCode:673)

给定一个未排序的整数数组,找到最长递增子序列的个数。

示例 1:

输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:

输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
注意: 给定的数组长度不超过 2000 并且结果一定是32位有符号整数。

方法一:先求一遍最长上升子序列,然后开一个sum数组统计最长上升子序列出现的次数即可,要注意sum数组的初始化,只有在dp[i]=1的地方sum[i]=1,其他初始都为0!

class Solution {
	
    public int findNumberOfLIS(int[] nums) {
        
    	if(nums.length<=1) return nums.length;
    	
    	int max=0,ans=0;
    	int n=nums.length;
    	int[] dp=new int[n];
    	int[] sum=new int[n];
    	
    	for(int i=0;i<n;i++)
    		dp[i]=1;
    	
    	for(int i=1;i<n;i++) {
    		for(int j=0;j<i;j++)
    			if(nums[j]<nums[i])
    				dp[i]=Math.max(dp[i], dp[j]+1);
    		max=Math.max(max, dp[i]);
    	}
    	sum[0]=1;
    	for(int i=1;i<n;i++) {
    		if(dp[i]==1)
    			sum[i]=1;
    		for(int j=0;j<i;j++)
    			if(nums[j]<nums[i] && dp[i]-1==dp[j])
    				sum[i]+=sum[j];
    	}
    	for(int i=0;i<n;i++)
    		if(dp[i]==max)
    			ans+=sum[i];
    	
    	return ans;
    }
}

方法二:线段树

首先,让我们称递增子序列的最长长度以及该区间中此类子序列的数量为区间的 “值”。
每个节点都知道它正在考虑的 nums 值的间隔 [node.range_left,node.range_right] 和 node.val
它包含有关区间的信息。节点还具有 node.left 和 node.right 子级,表示区间节点所考虑左右两部分。这些子节点根据需要创建。
现在,query(node, key) 将告诉我们由 node 指定的区间值,除非我们排除 key 以上的任何内容 。当 key 超出节点指定的区间时 ,我们返回答案。否则,我们将把区间划分为两个,查询两个区间,然后merge 结果。
merge(v1, v2) 的作用是什么?假设两个节点指定相邻的区间,并具有相应的值 v1 = node1.val, v2 = node2.val。v=merge(v1, v2) 应该是什么?如果一个节点中有较长的子序列,那么 v 就是它。如果两个节点都有长度相等的最长子序列,那么我们应该计算两个节点中的子序列。请注意,我们不必考虑产生较大子序列的情况,因为这些子序列是由 insert 处理的。
insert(node, key, val) 是什么功能?我们重复地将 key 插入节点指定的区间(可能是一个点),插入之后,该节点的值可能会更改,因此我们再次将这些值合并在一起。
最后,在我们的主算法中,对于 num in nums,我们 query 值 length、 num 以下区间的 count ,我们将计算 count 长度为 length + 1 的其他序列。然后我们用这些更新我们的树。

class Solution {
	
	class Value{
		int length;
		int count;
		public Value(int len,int ct) {
			length=len;
			count=ct;
		}
	}
	class node{
		Value val;
		node left,right;
		int range_left,range_right;
		public node(int start,int end) {
			left=null;
			right=null;
			range_left=start;
			range_right=end;
			val=new Value(0,1);
		}
		public int getRangeMid() {
			return range_left+(range_right-range_left)/2;
		}
		public node getLeft() {
			if(left==null) 
				left=new node(range_left,getRangeMid());
			return left;
		}
		public node getRight() {
			if(right==null) 
				right=new node(getRangeMid()+1,range_right);
			return right;
		}
	}
	
    public int findNumberOfLIS(int[] nums) {
    	
    	if(nums.length<=1) return nums.length;
    	
    	int min=nums[0],max=nums[0];
    	for(int num : nums) {
    		min=Math.min(min, num);
    		max=Math.max(max, num);
    	}
    	node root=new node(min,max);
    	for(int num : nums) {
    		Value v=query(root,num-1);
    		insert(root,num,new Value(v.length+1,v.count));
    	}
    	
    	return root.val.count;
    }
    
    public Value query(node root,int key) {
    	if(root.range_right<=key) return root.val;
    	else if(root.range_left>key) return new Value(0,1);
    	else return merge(query(root.getLeft(),key),query(root.getRight(),key));
    }
    
    public Value merge(Value v1,Value v2) {
    	if(v1.length==v2.length) {
    		if(v1.length==0) return new Value(0,1);
    		return new Value(v1.length,v1.count+v2.count);
    	}
    	return v1.length>v2.length?v1:v2;
    }
    
    public void insert(node root,int key,Value val) {
    	if(root.range_left==root.range_right) {
    		root.val=merge(val,root.val);
    		return;
    	}
    	else if(key<=root.getRangeMid())
    		insert(root.getLeft(),key,val);
    	else
    		insert(root.getRight(),key,val);
    	root.val=merge(root.getLeft().val,root.getRight().val);
    }
}
发布了987 篇原创文章 · 获赞 175 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/haut_ykc/article/details/104083894