选nico都对惹
题目描述
nico平时最喜欢说的口头禅是niconiconi~。有一天nico在逛著名弹幕网站"niconico"的时候惊异的发现,n站上居然有很多她的鬼畜视频。其中有一个名为《让nico为你脑的视频吸引了她的注意。她点进去一看,就被洗脑了
"niconicoh0niconico*^vvniconicoG(vniconiconiconiconiconicoG(vniconico......"弹幕中刚开始有很多“nico*1 nico*2”等计数菌,但到后面基本上都是“计数菌阵亡”的弹幕了。nico也想当一回计数菌。她认为:"nico" 计 a 分,"niconi" 计 b 分,"niconiconi" 计 c 分。她拿到了一个长度为 n 的字符串,请帮她算出最大计数分数。
注:已被计数过的字符不能重复计数!如"niconico"要么当作"nico"+"nico"计 a * 2 分,要么当作"niconi"+"co"计 b 分。
输入
19 1 2 5
niconiconiconiconi~
输出
7
思路:
我楞了半天没发现是个线性DP问题,tcl
dp[i]表示前i - 1个最大的分数,那么转移方程很明显,首先dp[i + 1] = dp[i],如果当前没有新的nico···出现就是前面的分数了,如果当前字母是o,并且前面有nic,说明当前位置出现了一个新的nico,那么dp[i + 1] = max(dp[i + 1],dp[i - 3] + a),就是把当前分数和前面的三个字母和当前的o拼成nico后加上前面的位置的分数
如果当前字母是i,并且前面是n,说明出现了一个ni,前面如果还出现了nico,说明出现了niconi,那么dp[i + 1] = max(dp[i + 1],dp[i - 5] + b);为什么不和nico + a分比较呢?这个肯定出现再niconi之前,那么上面的过程一定计算过了,每次都会把前面的分数直接给当前位置,所以不用比较了。
如果是i,前面是niconicon,说明构成了niconiconi,和上面一样,dp[i + 1] = max(dp[i + 1],dp[i - 8] + c,dp[i - 8] + a + b,dp[i - 8] + a + a)
最后输出dp[len]就好了
#include <bits/stdc++.h>
using namespace std;
const int N = 301000;
char s[N];
typedef long long ll;
ll dp[N];
ll n, a, b, c;
int main()
{
ios::sync_with_stdio(0);
cin >> n >> a >> b >> c;
cin >> s;
memset(dp, 0, sizeof dp);
int len = strlen(s);
for (int i = 0; i < len; i++){
dp[i + 1] = dp[i];
if(s[i] == 'o' && i - 3 >= 0){
if(s[i - 1] == 'c' && s[i - 2] == 'i' && s[i - 3] == 'n'){
dp[i + 1] = max(dp[i], dp[i - 2] + a);
}
}
else if(s[i] == 'i'){
if(i - 5 >= 0){
if(s[i - 1] == 'n' && s[i - 2] == 'o' && s[i - 3] == 'c' && s[i - 4] == 'i' && s[i - 5] == 'n'){
dp[i + 1] = max(dp[i + 1], dp[i - 4] + b);
}
}
if(i - 9 >= 0){
if(s[i - 1] == 'n' && s[i - 2] == 'o' && s[i - 3] == 'c' && s[i - 4] == 'i' && s[i - 5] == 'n'
&& s[i - 6] == 'o' && s[i - 7] == 'c' && s[i - 8] == 'i' && s[i - 9] == 'n'){
dp[i + 1] = max(dp[i + 1], dp[i - 8] + c);
dp[i + 1] = max(dp[i + 1], dp[i - 8] + b + a);
dp[i + 1] = max(dp[i + 1], dp[i - 8] + a + a);
}
}
}
}
cout << dp[len] << "\n";
return 0;
}