子序列的个数 --- 庞果网
庞果网的新题目:
题目描述
本题同样来自caopengcs,只要你有兴趣,每个人都可以出题(出题入口在主页右侧边栏“贡献题目”->“我要发布”内),以下是题目详情:
-
子序列的定义:对于一个序列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的不同子序列的数量。
输入:
- 长度为n的数组1<=n<=100,数组元素0<=a[i]<=110
输出:
- 子序列 的个数对1000000007取余数的结果(由于答案比较大,输出Mod 1000000007的结果即可)
算法描述
刚开始的时候我总想着直接去算排列组合,然后根据容斥原理把重复的去掉,从而得到结果,后来发现这样不是不行,要考虑的东西实在是太多了,编程起来非常麻烦。放弃了。
后来休息了一天,再看的时候换了一个思路,仔细分析了一下题目,按照递推关系,找到一个比较靠谱的思路。
设 f(k)为k长度的序列的子序列个数,那么很显然有以下推论:
- f(k)的个数包括f(k-1)的个数,因为f(k-1)的每一个都是f(k)的子序列,然后把f(k-1)的每个序列和a[k]组合起来,这些序列也是f(k)的子序列,个数还是f(k-1),载加上单独的a[k],那么
f(k)=2*f(k-1)+1
-
上面这个表达式是当a[k]和前面的数都不同的时候的情况,如果a[k]在前面出现过的话,那f(k)的个数除了上面那些的话:
- 还需要减去最近一次出现a[k]这个数值的地方-1的子序列个数,因为这些算重复了
- +1也没有了,因为f(a[k]上次出现的位置)包括了a[k]单独算一次的情况
f(k)=2*f(k-1)-f(a[k]上次出现的位置-1)
有了这两个表达式,就是一个完整的递推关系了,a[k]上次出现的位置的保存,可以用一个hash表来存储,这样速度很快,但是题目说a[k]的范围是0到220,那可以用一个220的数组来存储,反正也不会溢出,省得用hash了。
代码比较简单,具体的可以上github上看
subArray[0]=0; for(int i=1;i<=n;i++) { if(lastSameIndex[a[i-1]] == 0 ) { subArray[i]=(subArray[i-1]*2)+1; } else { subArray[i]=((subArray[i-1]*2)-subArray[lastSameIndex[a[i-1]]-1]); } lastSameIndex[a[i-1]]=i; }