【算法刷题】子序列个数

子序列的定义:对于一个序列a=a[1],a[2],......a[n],则非空序列a'=a[p1],a[p2]......a[pm]为a的一个子序列,其中1<=p1<p2<.....<pm<=n。

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


解题:动态规划,设前k个数的子序列个数为d[k],可列出转移方程:

1)当a[k]与前K-1个数都不相同时,只用考虑加入a[k]时个数情况:

       d[k]=d[k-1](原本有的个数)+d[k-1](加入a[k]增加的个数)+1(a[k]单独成序列的个数)

             =2d[k-1]+1

2)当a[k]与前边有重复的数时,首先依旧要加上d[k-1],但是加入a[k]要减去重复的子序列,只需找到离a[k]最近相同的那个数a[t],则,加入a[k]时,重复的个数为a[t-1]+1.....我们是从1 - n对数组进行遍历的,计算d[]就是从1到n依次计算的,那么第一次遇到a[k] = a[t]的情况满足条件:有且仅有一个t使得a[t] = a[k],之后每次遇到相同的时,之前的相同的已经减去了重复的个数,因此只用找到最近的那个相同的数即可。

      d[k]=2d[k-1]+1-(d[t-1]+1) 

            =2d[k-1]-d[t-1]

注:在实际编程的过程中,如何找到最近相同的数的下标呢???可用一个数组存储原数组中的数字最后一次出现的下标的位置


#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;



int countsub(int a[],int len,int maxn)
{
	
	if (len == 0)
		return 0;

	int *d = new int[len+1];
	int *b = new int[maxn+1];

	for (int i = 0; i <= maxn; ++i)
	{
		b[i] = -1;
		
	}
	b[a[0]] = 1;
	d[1] = 1;
	d[0] = 0;

	
	for (int i = 2; i < len+1 ; ++i)
	{
		if (b[a[i-1]] == -1)
		{
			d[i] = 2 * d[i - 1] + 1;
		}
		else
		{
			d[i] =( 2 * d[i - 1]) - d[b[a[i-1]]-1];
		}
		b[a[i-1]] = i;

	}

	int res = d[len];
	delete[] d;
	delete[] b;
	return res;
}

int main()
{
	int n;
	cin >> n;
	int *a = new int[n];
	int max(0);
	for (int i = 0; i <n; ++i)
	{
		int temp;
		cin >> temp;
		max = temp > max ? temp : max;
		a[i] = temp;
	}
	
	int sum = countsub(a,n,max);
	cout << sum << endl;
	
	
}


猜你喜欢

转载自blog.csdn.net/neo_dot/article/details/80722751