问题定义:
对于一个序列a=a[1], a[2] ,..., a[n]。任意子序列可表示为{aPk, aPk+1, ..., aPk+m},其中 1<= Pk < Pk+1< Pk+m<=n, 对于给定的序列,求不同的子序列的个数。
思路
令 f(i) 表示 原序列a中前 i 个数中含有的不同子序列个数,则
- 若原序列中第i+1个数与前 i 个数都不相同,则 f(i+1) = f(i)+f(i)+1.
这是因为,第i+1个数可以和前 i 个数中构成的 f(i)个不同的子序列结合形成新的f(i)个子序列,且第i+1个数自己可以构成一个子序列, 再加上原来的f(i)个子序列,就是原序列中前i+1个数中含有不同子序列的个数。
- 若原序列中第i+1个数与前 i 个书中存在相同的数, 则 f(i+1)=f(i)+f(i)-f( last(a[i+1]) ), 其中 last(a[i+1])是数字a[i+1]在原序列中最后一次出现的位置。
之所以减去f(last(a[i+1]) ),是因为 a[i+1] 和前 last(a[i+1])-1个数字组合形成的子序列与a[last(a[i+1])] 和前 last(a[i+1])-1个数字形成的子序列相同。
e.g. 对序列{1, 2,2, 5, 4, 2, 3}, 令i+1=6, 则a[i+1]=2, 那么last(a[i+1])=3;
a[i+1]=26 (6代表其下标), 因为前 last (a[i+1])=3个数字:1, 2, 23 组成的子序列三个:{1}, {2},{1,2}。而a[i+1] 和前 last(a[i+1])-1 (=2)个数字组合形成的子序列和这三个相同,因此需要减去相同的这一部分.
代码如下:
final MaxLen = 99999;
int[] last = new int[MaxLen]; // 假设初始化为-1
int[] f = new int[MaxLen];
// Read inputs
int n=scan.nextInt(); // read the length of the seq
for(int i=1; i<=n; ++i){
int x = scan.nextInt();
if(last[x]!=-1){
f[i] = f[i-1]*2 - f[last[x]];
}else{
f[i] = f[i-1]*2+1;
}
last[x]=i;
}