【枚举区间思想+DP】子串的子序列

news/2024/11/28 22:41:40/

F-子串的子序列_牛客小白月赛62 (nowcoder.com)

题意:

 

思路:

复盘一下应该有的思路:

首先n^2枚举肯定超时,我们枚举的是一个区间

枚举区间有一些trick:

1.枚举其中一个右(左)端点,O(1)或O(logn)计算满足条件的左(右)端点个数 ,可以组合数学 or DP 计算

2.单独计算每个元素的贡献,O(1)或O(logn)计算合法区间的左端点和右端点匹配个数,这种一般是乘法原理

在这里我们用到的是第一种思想

我们去枚举右端点r,然后计算满足条件的左端点的个数

我就想到这里,甚至连DP都没想到

这里的DP并不是传统意义的子序列DP,也就是说,并不是枚举两个指针然后转移(LIS),也不是统计前缀满足某个性质的子序列的最值(接龙数列 or 绝世好题),这里用的是状态机DP

设dp[i][0/1][0/1]表示,前i个字符,a的数量的奇偶性是0/1,ac的数量的奇偶性是0/1的子串个数

为什么是这样设计的,其实一开始想的应该只是ac的数量的奇偶性,但是发现这样不能算贡献,考虑多一个字符c,贡献还与前缀a的数量的奇偶性有关,所以应该还有加上一维

这里告诉我们,在考虑计算贡献的DP时,考虑多一格,然后思考如何计算贡献

然后转移的时候注意这是子串数量,所以应该从i-1转移

(看代码)为什么一开始要++,因为考虑新加的字符自成一个区间

还有一个坑点:

然后统计答案的时候会把ac子序列个数为0的区间个数算进去,因此我们需要减去这些区间

这个怎么做?同样也是那个枚举区间的trick,我们去枚举左区间,右侧ac的c左边那格-i+1就是区间个数了,统计一下即可

Code:

#include <bits/stdc++.h>#define int long longusing namespace std;using i64 = long long;const int mxn=5e5+10;
const int mxe=5e5+10;
const int mod=1e9+7;string s;int N;
int pre_a[mxn],pre_c[mxn];
int dp[mxn][2][2];int no_ac(){int res=0;for(int i=1;i<=N;i++){if(s[i]=='a'){pre_a[i]=i;pre_c[i]=pre_c[i-1];}else if(s[i]=='c'){pre_c[i]=i;pre_a[i]=pre_a[i-1];}else{pre_a[i]=pre_a[i-1];pre_c[i]=pre_c[i-1];}}for(int r=1;r<=N;r++) res+=r-(pre_a[pre_c[r]]+1)+1;return res;
}
void solve(){cin>>s;N=s.size();s=" "+s;int ans=0;for(int i=1;i<=N;i++){if(s[i]=='a'){dp[i][1][0]++;dp[i][1][1]+=dp[i-1][0][1];dp[i][1][0]+=dp[i-1][0][0];dp[i][0][1]+=dp[i-1][1][1];dp[i][0][0]+=dp[i-1][1][0];}else if(s[i]=='c'){dp[i][0][0]++;dp[i][1][1]+=dp[i-1][1][0];dp[i][1][0]+=dp[i-1][1][1];dp[i][0][1]+=dp[i-1][0][1];dp[i][0][0]+=dp[i-1][0][0];}else{dp[i][0][0]++;for(int j=0;j<2;j++){for(int k=0;k<2;k++){dp[i][j][k]+=dp[i-1][j][k];}}}ans+=dp[i][1][0]+dp[i][0][0];}cout<<ans-no_ac()<<'\n';
}
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int __=1;//cin>>__;while(__--)solve();return 0;
}


http://www.ppmy.cn/news/451482.html

相关文章

科达制造和盐湖股份的事儿

聊一下科达制造和盐湖股份的事儿吧。 反正挺扑朔迷离的。 但是今天科达制造一周之内第二次跌停&#xff0c;多少代表了一点预期&#xff0c;虽然不一定是事实。 上次发了个帖子&#xff0c;可不是传谣&#xff0c;我并没有证实或者相信什么&#xff0c;只是就科达制造和盐湖股份…

kedacom录像机使用方法_科达(KEDACOM)NVR2860 64路网络硬盘录像机测评

NVR2860采用标准的2U机箱,集视觉冲击温和的黑色铝合金与整齐的结构排列于一体,给人以耐看大气之感。细察之下,黑色铝铁合金的材质较好的避免了外盖易被刮伤的困扰,前面板整齐的排列着8个SATA硬盘插槽,最大容量可达32T,且每个硬盘支持HOT-SWAP热插拔,安装和调试、维护都极…

feign 基于参数动态指定路由主机

feign 基于参数动态指定路由主机 背景 项目上最近有需求&#xff1a;通过一个公共基础实体定义一个主机地址字段 , feign 远程调用时候根据具体值动态改变进行调用。 官方解决方案 第一种方案 官方支持动态指定 URI Overriding the Request Line If there is a need to tar…

CB备份一体机恢复

一、查看备份情况。 点击虚拟机主机名&#xff0c;查看详细 二、选中还原&#xff0c;展开文件系统。 三、点击主机名&#xff0c;查看备份详情&#xff0c;点击恢复。 四、异地恢复

监测机房蓄电池,原来这么简单

机房作为存储、交换、传输等所有信息网络工程功能的载体&#xff0c;必须对机房内的物理运行环境、配电、设备运行、人员活动、消防情况等进行24小时实时监控和智能调控。 传统蓄电池管理3大常见弊端 01.机房发生停电、市电异常等情况时&#xff0c;蓄电池用电数据没有实时监控…

电池管理系统(BMS)功能与作用/BMS 故障分析方法/15种常见故障案例分析

提示&#xff1a;本篇文章仅供学习参考 文章目录 一、电池管理系统&#xff08;BMS&#xff09;功能与作用二、BMS 故障分析方法三、15种常见故障案例分析 一、电池管理系统&#xff08;BMS&#xff09;功能与作用 从整车角度&#xff0c;电池管理系统&#xff08;BMS&#xf…

新能源锂电池极片制造设备如何实现故障智能诊断?

近年来&#xff0c;受国家新能源政策以及新能源汽车的快速发展&#xff0c;锂电池产能急剧上升&#xff0c;这对锂电企业与相关生产设备都是极大的考验。本文主要基于PreMaint在锂电行业的大量实践&#xff0c;分享锂电池极片制造设备实现故障智能诊断和智能运维的可落地方案。…

笔记本电池修复常见方法大全

笔记本电池用一段时间之后都会出现不同程度的损伤&#xff0c;在某些不利于电池运行的情况下甚至损耗率可以达到60%以上&#xff0c;在这种情况下电池一般支撑不了40分钟&#xff0c;很不方便&#xff0c;怎么修复笔记本电池&#xff1f;下面就为大家介绍笔记本电池修复常见方法…