题目: 给出n个数, a1~an,要求出不同的子序列个数。
比如(3 2 4 5 1),其中(3 2) (3 4 5)就是其中的子序列。
题解:很容易想到要使用动态规划。
设 dp[i] 表示 到 i 为止,不同的子序列个数, dp[i-1] 表示到 i-1 为止,不同的子序列个数
那么dp[i] 肯定是由 dp[i-1] 转移得到,那么状态转移方程怎么写呢?
试想,对于 a[i],如果这个数第一次在子序列中出现,也就是我们第一次遍历到 a[i] 时,那么 a[i] 可以和 前面 i-1 个数都能形成一个新的子序列,所以新增加的子序列个数就是前面已经求过的有 dp[i-1]个子序列,然后再加上当前a[i] 本身构成的子序列,那么就可以列出第一个方程:
当a[i] 被第一次遍历到时, dp[i] = dp[i-1] + (新增加的个数)dp[i-1] + 1;
如果 a[i] 在之前已经被遍历过了,(比如例子: 1 3 4 5 3 2)。假设当前遍历到a[5] = 3,但是a[5] = a[2] = 3,就是说3在坐标5之前的2位置已经被遍历过,如果此时再按上述方程转移,就会出现重复情况,所以我们需要减去 当前遍历到的3之前一次3所计算出的dp值,所以我们需要记录最后一次出现数字x的位置。
具体看看代码或者手动模拟一下就知道了
(PS:代码的题面其实是求给定01串不同的子串个数,其实做法都是一样的)
last数组记录 当前值最后一次出现的位置。
/* ***********************************************
Author :pall_scall
Created Time :2019年04月06日 星期六 09时51分26秒
File Name :fjut.cpp
************************************************ */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <string>
using namespace std;
typedef long long ll;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&-x
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
const double eps = 1e-6;
const double pi = acos(-1.0);
char s[maxn];
ll dp[maxn];int last[maxn];
int n;
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d",&n)){
scanf("%s",s+1);
dp[0] = 0;
mem(last,-1);
for(int i = 1; i <= n; i++){
if(last[s[i]-'0'] != -1){
dp[i] = (dp[i-1]*2 - dp[last[s[i]-'0']-1]+mod)%mod;
}else{
dp[i] = (dp[i-1]*2 + 1+mod)%mod;
}
last[s[i]-'0'] = i;
}
cout<<dp[n]%mod<<endl;
}
return 0;
}