dp之 不同的子序列个数

题目: 给出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;
}

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值