数组子序列的个数

数组子序列的个数,比如4,14,2,3和14,1,2,3都为4,13,14,1,2,3的子序列。 对于给出序列a,有些子序列可能是相同的,这里只算做1个,要求输出a的不同子序列的数量。

分析:如果对一个数组后面添加一个与前面都不同的数字构成一个新的数组,那么新的数组的子序列就是前面的子序列的个数乘以2,再加上新添加的数字;如果后面添加的数字前面出现过,那么按照上面的计算方式中存在重复的,将重复的剪掉即可:一个是自身,一个是离自身最近的与自身形同的数字对应的子序列数,比如1,2,3,4,5,6,4,7,8,4;那么就应该减去1,2,3,4,5,6,4这段所有的子序列;因为1,2,3,4,5,6与后面第一个4可以形成的序列与最后一个4也可以形成;

代码如下:

//  [10/2/2013 qingezha] 数组子序列的个数,4,14,2,3和14,1,2,3都为4,13,14,1,2,3的子序列。 对于给出序列a,有些子序列可能是相同的,这里只算做1个,要求输出a的不同子序列的数量。
//	用一个数组记录出现2次或2次以上的数字,离当前数字最近的相同的数字的下标
//	比如1 2 3 4 2,一开始都为0,然后下标一次变为1 2 3 4,到新2 的时候 因为有2,所以要找到这个2的下标,才可以运算那个式子
//	所以用last_index记录数字的下标,里面是对应的arr[i],即要找的值2
#define Mod 1000000007
long sub_sequence(int *arr,int len)
{
	long sub_arr[120] = {0};						
	int last_index[120] = {0};						//初始值这里设的好
	for (int iter=1;iter<=len;++iter)
	{
		switch(last_index[arr[iter-1]])
			{
		case 0:
			{
				sub_arr[iter] = 2 * sub_arr[iter-1]+ 1;
				break;
			}
		default:
			{
				sub_arr[iter] = 2 * sub_arr[iter-1] - sub_arr[last_index[arr[iter-1]]-1];//上一个相同的数字的下标
			}
		}
		last_index[arr[iter-1]] = iter;				//这里写错了last_index[iter-1],每一次都更新,按次序递增
	}
	return sub_arr[len];							//这里写错了 sub_arr[len-1]
	//////test///////////////////////////////////////////////////////////////////
	//int arr[20] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
	//cout<<sub_sequence(arr,sizeof(arr)/sizeof(int));
	//////////////////////////////////////////////////////////////////////////

}


### 最长递增子序列个数算法实现 为了计算最长递增子序列个数,通常采用动态规划的方法。该方法的核心在于维护两个数组 `dp` 和 `count`: - **`dp[i]`** 表示以第 `i` 个元素结尾的最长递增子序列的长度。 - **`count[i]`** 表示以第 `i` 个元素结尾的最长递增子序列的数量。 以下是基于此思路的一个典型实现[^3]: ```cpp class Solution { public: int findNumberOfLIS(vector<int>& nums) { int n = nums.size(); if (n == 0) return 0; vector<int> dp(n, 1); // 记录以每个位置结尾的最长递增子序列长度 vector<int> count(n, 1); // 记录以每个位置结尾的最长递增子序列数量 int maxLength = 1; // 当前已知的最大长度 for (int i = 1; i < n; ++i) { for (int j = 0; j < i; ++j) { if (nums[j] < nums[i]) { // 如果当前元素大于之前的某个元素,则可能形成更长的递增子序列 if (dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; // 更新长度 count[i] = count[j]; // 同时更新对应的计数值 } else if (dp[j] + 1 == dp[i]){ count[i] += count[j]; // 长度相等时累加计数 } } } maxLength = max(maxLength, dp[i]); // 更新全局最大长度 } int result = 0; // 总结所有具有最大长度的子序列数量 for (int i = 0; i < n; ++i){ if (dp[i] == maxLength){ result += count[i]; } } return result; } }; ``` #### 关键点解析 1. **状态转移方程** - 对于每一个元素 `nums[i]`,遍历其之前的所有元素 `nums[j]` (`j < i`) 来判断是否存在递增关系(即 `nums[j] < nums[i]`)。如果存在这样的关系,则尝试更新 `dp[i]` 的值以及相应的计数器 `count[i]`[^4]。 2. **时间复杂度分析** - 外层循环运行 `O(n)` 次,内层循环同样最多执行 `O(n)` 步骤,因此总体的时间复杂度为 \( O(n^2) \)[^5]。 3. **空间复杂度分析** - 使用了两个辅助数组 `dp` 和 `count`,它们的空间需求均为线性级别,故总空间复杂度为 \( O(n) \). ### 示例解释 假设输入序列为 `[1, 3, 5, 4, 7]`: - 初始状态下,`dp=[1,1,1,1,1]`, `count=[1,1,1,1,1]`. - 经过迭代处理后得到的结果如下所示: - `dp=[1,2,3,3,4]` - `count=[1,1,1,1,2]`. 最终返回结果应为 `2`,因为有两个不同的最长递增子序列:`[1, 3, 5, 7]` 和 `[1, 3, 4, 7]`[^3]. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值