题目链接

题意 : 给出长度为 n 的字符串、问你准确删除 m 个元素之后、能产生多少种不同的子串

分析 ( 参考博客 ): 

可以考虑线性 DP 解决这个问题

试着如下定义动态规划数组

dp[i][j] = 在加入第 i 个字符串后、总共删除了 j 个字符后的不同子串的个数

不难写出状态转移方程 dp[i][j] = dp[i-1][j] + dp[i-1][j-1]

代表在 i 这个字符加入之后、在删除总次数为 j 的情况下是否删除 i 的两个状态转移而来

但是这样子势必会有重复的字串出现、例如 aaa 这个字符串 删除第一个和第二个都产生 aa

所以需要减去重复的部分、注意到如果在第 i 个字符之前后一个和它相同的字符出现记做 pre_i

而且满足 (i字符位置) - (pre_i字符位置) <= j 则说明会有重复计算的情况

因为如果在总共删除的 j 个字符中包含了pre_i ~ i 之间的字符、那么便会产生重复

例如 abcexxe 发现有一前一后两个 e 两个之间相隔有两个 x

那么如果用上述的转移方程去更新的话 dp[4][0] = "abce" 且 dp[7][3] = 包含了 "abce"

所以会有诸如这样子的重复、减去就行了

怎么减? if  (i字符位置) - (pre_i字符位置) <= j   执行  dp[i][j] -= dp[ pre_i - 1 ][ j - ( i - pre_i ) ]

因为先前重复计算的部分就包含了 pre_i 位置下减去了 j - ( i - pre_i ) 个字符的部分

#include<bits/stdc++.h>
#define LL __int64
#define ULL unsigned long long

#define scl(i) scanf("%lld", &i)
#define scll(i, j) scanf("%lld %lld", &i, &j)
#define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k)
#define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l)

#define scs(i) scanf("%s", i)
#define sci(i) scanf("%d", &i)
#define scd(i) scanf("%lf", &i)
#define scIl(i) scanf("%I64d", &i)
#define scii(i, j) scanf("%d %d", &i, &j)
#define scdd(i, j) scanf("%lf %lf", &i, &j)
#define scIll(i, j) scanf("%I64d %I64d", &i, &j)
#define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k)
#define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k)
#define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k)
#define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l)
#define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l)
#define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l)

#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define lowbit(i) (i & (-i))
#define mem(i, j) memset(i, j, sizeof(i))

#define fir first
#define sec second
#define VI vector<int>
#define ins(i) insert(i)
#define pb(i) push_back(i)
#define pii pair<int, int>
#define VL vector<long long>
#define mk(i, j) make_pair(i, j)
#define all(i) i.begin(), i.end()
#define pll pair<long long, long long>

#define _TIME 0
#define _INPUT 0
#define _OUTPUT 0
clock_t START, END;
void __stTIME();
void __enTIME();
void __IOPUT();
using namespace std;
;
;

int arr[maxn];
int Pre[maxn];
 + ];
 + ];

int main(void){__stTIME();__IOPUT();

    int n, m, k;

    while(~sciii(n, m, k)){

        ; i<=n; i++) Last[i] = ;

        ; i<=n; i++){
            sci(arr[i]);
            Pre[i] = Last[arr[i]];
            Last[arr[i]] = i;
        }

        ; i<=m; i++) dp[i][i] = ;
        ///代表空串、在下面的转移方程中 dp[i-1][j] 会用到
        ///其意义是更新到 i 为止在不删除第 i 个字符情况下总共删除了 j 个
        ///字符的情况、此时就说明只剩下第 i 个字符、dp数值应该为 1
        ///故给空串赋值为 1

        ; i<=n; i++){
            dp[i][] = ;
            ; j<=min(m, i-); j++){
                dp[i][j] = (dp[i-][j-]%mod + dp[i-][j]%mod) % mod;
                if(Pre[i] && i - Pre[i] <= j){
                    dp[i][j] = (dp[i][j] - dp[ Pre[i] -  ][ j - (i - Pre[i]) ] + mod ) % mod;
                }
            }
        }

        printf("%d\n", dp[n][m]%mod);
    }

__enTIME();;}

void __stTIME()
{
    #if _TIME
        START = clock();
    #endif
}

void __enTIME()
{
    #if _TIME
        END = clock();
        cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl;
    #endif
}

void __IOPUT()
{
    #if _INPUT
        freopen("in.txt", "r", stdin);
    #endif
    #if _OUTPUT
        freopen("out.txt", "w", stdout);
    #endif
}

Nowcoder Removal ( 字符串上的线性 DP )的更多相关文章

  1. Codeforces 176B (线性DP+字符串)

    题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=28214 题目大意:源串有如下变形:每次将串切为两半,位置颠倒形成 ...

  2. 动态规划——线性dp

    我们在解决一些线性区间上的最优化问题的时候,往往也能够利用到动态规划的思想,这种问题可以叫做线性dp.在这篇文章中,我们将讨论有关线性dp的一些问题. 在有关线性dp问题中,有着几个比较经典而基础的模 ...

  3. 动态规划_线性dp

    https://www.cnblogs.com/31415926535x/p/10415694.html 线性dp是很基础的一种动态规划,,经典题和他的变种有很多,比如两个串的LCS,LIS,最大子序 ...

  4. 线性DP详解

    顾名思义,线性DP就是在一条线上进行DP,这里举一些典型的例子. LIS问题(最长上升子序列问题) 题目 给定一个长度为N的序列A,求最长的数值单调递增的子序列的长度. 上升子序列B可表示为B={Ak ...

  5. 洛谷P1140 相似基因(线性DP)

    题目背景 大家都知道,基因可以看作一个碱基对序列.它包含了444种核苷酸,简记作A,C,G,TA,C,G,TA,C,G,T.生物学家正致力于寻找人类基因的功能,以利用于诊断疾病和发明药物. 在一个人类 ...

  6. LightOJ1044 Palindrome Partitioning(区间DP+线性DP)

    问题问的是最少可以把一个字符串分成几段,使每段都是回文串. 一开始想直接区间DP,dp[i][j]表示子串[i,j]的答案,不过字符串长度1000,100W个状态,一个状态从多个状态转移来的,转移的时 ...

  7. hdu1712 线性dp

    //Accepted 400 KB 109 ms //dp线性 //dp[i][j]=max(dp[i-1][k]+a[i][j-k]) //在前i门课上花j天得到的最大分数,等于max(在前i-1门 ...

  8. 『最大M子段和 线性DP』

    最大M子段和(51nod 1052) Description N个整数组成的序列a[1],a[2],a[3],-,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的.如果M &g ...

  9. poj2228 Naptime【(环结构)线性DP】

    Naptime Time Limit: 1000MS   Memory Limit: 65536K Total Submissions:3374   Accepted: 1281 Descriptio ...

随机推荐

  1. 基于rabbitmq的Spring-amqp基本使用

    目录 1. 依赖和配置 添加AMQP的启动器: 在application.yml中添加RabbitMQ地址: 2. 监听者 3. AmqpTemplate 4. 测试代码 Spring-amqp是对A ...

  2. 04: gitlab安装与使用

    1.1 gitlab安装(192.168.56.12中安装) 1.GitLab是什么? 1. GitLab实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目. 2. GitLa ...

  3. linux基础命令<二>

    1.关机 init 0   poweroff   halt  shutdown –h   now 2.重启 init 6   reboot  shutdown –r now 3.查询都有那些用户在系统 ...

  4. Collection接口的子接口——Queue接口

    https://docs.oracle.com/javase/8/docs/api/java/util/Queue.html public interface Queue<E> exten ...

  5. oracle查看执行计划以及使用场景

    文档结构: oracle执行计划使用场景 环境: Centos 6.10 Oracle 18.3.0.0.0 c 11g默认启动了自动统计信息收集的任务,默认运行时间是周一到周五晚上10点和周6,周天 ...

  6. 如何同步多个 git 远程仓库

    请看 -> 如何同步多个 git 远程仓库

  7. LintCode 29---交叉字符串

    public class Solution { /** * @param s1: A string * @param s2: A string * @param s3: A string * @ret ...

  8. 转载:Linux目录文件的权限查看与修改

    ########################################                                                             ...

  9. python删除数组中元素

    有数组a,要求去掉a所有为0的元素 a = [2,4,0,8,9,10,100,0,9,7] Filter a= filter(None, a) Lambada a = filter(lambda x ...

  10. IT经理工作职责

    IT经理工作职责:     1. 管理公司信息技术以及计算机系统.   2. 确保公司信息技术是可访问的并且配备了现有的可用的硬件和软件.   3. 监控并且维护公司信息技术并确保能够得到最大化的使用 ...