子序列的定义:对于一个序列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; }