[算法模版]子序列DP

[算法模版]子序列DP

如何求本质不同子序列个数?

朴素DP

复杂度为\(O(nq)\)。其中\(q\)为字符集大小。

\(dp[i]\)代表以第\(i\)个数结尾的本质不同子序列个数。注意,这里对于每一个字符,只计算上一个相同字符带来的贡献。如果全部计算的话会算重复。

最后统计答案的时候也只统计每个字符最后一次出现的位置的答案。

例题:【线上训练13】子序列 中的50分部分分

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=2e6+100,mod=998244353;
typedef long long ll;
char s[2][maxn],ori[maxn];
int n,len=1;
int last[30];
ll dp[maxn];
void init() {
    scanf("%s",ori+1);
    s[1][1]=ori[1];
    for(int i=2;i<=n;i++) {
        int md=i&1;
        for(int j=1;j<=len;j++) {
            s[md][2*j-1]=ori[i];
            s[md][2*j]=s[md^1][j];
        }
        s[md][2*len+1]=ori[i];
        len=2*len+1;
    }
}
int main() {
    scanf("%d",&n);
    init();
    scanf("%s",s[n&1]+1);len=strlen(s[n&1]+1);
    for(int i=1;i<=len;i++) {
        int ty=s[n&1][i]-'a';
        dp[i]=1;
        for(int j=0;j<26;j++) {
            if(last[j]){
                dp[i]+=dp[last[j]];
            }
        }
        dp[i]%=mod;
        last[ty]=i;
    }
    ll ans=0;
    for(int i=0;i<26;i++) {
        if(last[i])ans+=dp[last[i]];
        ans%=mod;
    }
    ans%=mod;
    printf("%lld",ans);
}

优化DP

我们令\(f[i]=\sum_{j=1}^q dp[last[q]]\)。其中\(q\)为字符集大小

转移的时候有两种情况:

  1. 当前字符未出现过(第一个)。那么令\(dp[i]=f[i-1]+1\),则\(f[i]=2\times f[i-1]+1\)。
  2. 当前字符在原来出现过。那么令\(dp[i]=f[i-1]+1\)。因为只记录最后一次,所以在给\(f\)加上这一次的\(dp\)值之后还要删去上一次的\(dp\)值。\(f[i]=2\times f[i-1]-dp[last[i]]+1\)。因为\(dp[last[i]]=f[last[i]-1]+1\),所以\(f[i]=2\times f[i-1]-f[last[i]-1]\)

其实本质就是利用子序列DP每次增加的都是\(\sum dp[每一个字符]\)这个特性,每次转移将\(\sum dp[每一个字符]\)用\(O(1)\)复杂度起来,做到\(O(1)\)的转移。

算法复杂度\(O(n)\)

例题:FZU-2129

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,a[1000500],f[1000500],last[1000500],ans=0;
const int mod=1e9+7;
int main() {
    ios::sync_with_stdio(0);
    while(cin>>n) {
        for(int i=1;i<=n;i++)cin>>a[i];ans=0;
        memset(f,0,sizeof(f));memset(last,0,sizeof(last));
        for(int i=1;i<=n;i++) {
            if(!last[a[i]])f[i]=(2ll*f[i-1]+1)%mod;
            else f[i]=(2ll*f[i-1]-f[last[a[i]]-1])%mod;
            last[a[i]]=i;
        }
        ans=(f[n]%mod+mod)%mod;
        cout<<ans<<endl;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/GavinZheng/p/11777231.html

时间: 2024-11-01 11:11:05

[算法模版]子序列DP的相关文章

[算法模版]AC自动机

[算法模版]AC自动机 基础内容 板子不再赘述,OI-WIKI有详细讲解. \(query\)函数则是遍历文本串的所有位置,在文本串的每个位置都沿着\(fail\)跳到根,将沿途所有元素答案++.意义在于累计所有以当前字符为结尾的所有模式串的答案.看代码就能很容易的理解. 另外\(e[i]\)记录的是第\(t\)个模式串结尾是哪个节点(所有节点均有唯一的编号). 贴个P5357 [模板]AC自动机(二次加强版)板子: #include<iostream> #include<cstdio&

kmp算法模版

kmp算法模版 kmp是在字符串s中匹配字符串t的一种高效算法,朴素的暴力是o(n*m) ,kmp复杂度是o(n+m) /** 调用 ans=kmp(s,t),s为原串,返回t在s的位置(首位置为1),找不到着返回0. */ char s[maxn],t[maxn]; int next[maxn]; void get_next(char* t) { next[1]=0; for(int i=1,j=0;i<strlen(t);){ if(j==0||t[i-1]==t[j-1]){ i++;j+

HDU 1231 最大连续子序列 DP题解

典型的DP题目,增加一个额外要求,输出子序列的开始和结尾的数值. 增加一个记录方法,nothing special. 记录最终ans的时候,同时记录开始和结尾下标: 更新当前最大值sum的时候,更新开始节点. const int MAX_N = 10001; long long arr[MAX_N]; int N, sta, end; long long getMaxSubs() { long long sum = 0, ans = LLONG_MIN; int ts = 0; for (int

lis(最长上升子序列) dp

lis(最长上升子序列) dp 求序列的lis,子序列可不连续 for(int i=1;i<=N;i++){ scanf("%d",&a[i]); dp[i]=1; } for(int i=2;i<=N;i++){ for(int j=1;j<i;j++){ if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+1); } } int ans=1; for(int i=1;i<=N;i++){ //注意并不是dp[N]最大,而是要

Hopcroft-Karp算法模版

#include <cstdio> #include <cstring> #include <vector> #include <queue> #include <stack> #include <cmath> using namespace std; const int maxn = 3010; const int INF = 1<<28; int dx[maxn], dy[maxn]; int cx[maxn], cy

Codeforces 10D LCIS 求最长公共上升子序列及输出这个子序列 dp

题目链接:点击打开链接 题意: 给定n长的一个序列 再给定k长的一个序列 求LCIS并输出这个子序列 如有多解输出任意解.. = - = 敲的时候听着小曲儿pre的含义还没有想清楚,万万没想到就过了... #include<stdio.h> #include<iostream> #include<string.h> #include<set> #include<vector> #include<map> #include<mat

带权二分图的最大权匹配 KM算法模版

带权二分图的最大权匹配 KM算法模版 下面是kuangbin大神的模版,已通过西电oj1048的测试 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set

HDU 1160 FatMouse&#39;s Speed (最長單調子序列 dp)

鏈接: http://acm.hdu.edu.cn/showproblem.php?pid=1160 Problem Description FatMouse believes that the fatter a mouse is, the faster it runs. To disprove this, you want to take the data on a collection of mice and put as large a subset of this data as pos

bfs算法模版

bfs算法模版 写过很多bfs题,每次写bfs代码习惯都略有不同,有些糟糕的代码习惯影响了解题速度 下面这份简单的三维bfs可以算是写得比较不错的一份了,以后按这种习惯写,虽然没有写回溯路径,但回溯路径很简单,只要加个fa数组就行了,所以就不加在模版上了 //bfs模版 int X,Y,Z; char ch[maxn][maxn][maxn]; bool vis[maxn][maxn][maxn]; int sx,sy,sz; int ans; int dx[]={-1,1,0,0,0,0};