退阶:
给出一个仅含
N
,
O
,
I
N,O,I
N,O,I的字符串,问其子序列中有多少个
N
O
I
串
NOI串
NOI串?
例:串
N
N
O
O
I
I
NNOOII
NNOOII 中一共有8个
N
O
I
NOI
NOI。
思考:
计算NOI的个数,每遇到一个I,都要加上前面NO的个数。
计算前面NO的个数,每遇到一个O,都要加上前面N的个数。
于是就是前缀和的思路。
Code:
#include <iostream>
using namespace std;
const int N = 200010, mod=1e9+7;
int T, n, m;
char a[N];
int main(){
cin>>n;
ll ans=0,cnt1=0,cnt2=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]=='N') cnt1++;
else if(a[i]=='O') cnt2+=cnt1;
else ans+=cnt2;
}
cout<<ans;
return 0;
}
进阶:
给出一个仅含 N , O , I N,O,I N,O,I的字符串,再将“N”,“O”,“I”中的任一个插入到此字符串中的任意位置,最多能够得到多少NOI串?
思考:
观察发现,肯定是加N划算,而且是在最前面加。
但是原来串可能凑不成一个,这种情况下就要先凑成一个。
所以原来的想法是,如果能组成一个NOI串,就加N,否则少啥加啥。
但是并不能方便准确地找出少的是啥。
干脆枚举三种情况,取三个答案最大值。
如果加N,加到最前面。
如果加I,加到最后面。
如果加O,加到串中间。
//注意添加O的情况,要枚举添加的位置,答案为左边 N N N的个数与右边 I I I的个数的乘积。
Code:
#include <iostream>
using namespace std;
const int N = 2000010, mod=1e9+7;
int T, n, m;
char a[N];
int f1[N],f2[N];
int main(){
cin>>n;
ll ans=0,cnt1=0,cnt2=0,flag=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
f1[i]=f1[i-1];
if(a[i]=='N') f1[i]++;
}
for(int i=n;i>=1;i--){
f2[i]=f2[i+1];
if(a[i]=='I') f2[i]++;
}
//最前面加'N'
cnt1=1;
for(int i=1;i<=n;i++)
{
if(a[i]=='N') cnt1++;
else if(a[i]=='O') cnt2+=cnt1;
else ans+=cnt2;
}
//最后面加'I'
cnt1=cnt2=0;
ll t=0;
for(int i=1;i<=n;i++)
{
if(a[i]=='N') cnt1++;
else if(a[i]=='O') cnt2+=cnt1;
else t+=cnt2;
}
t+=cnt2;
ans=max(ans,t);
//中间加'O',枚举加的位置
for(int i=1;i<=n;i++){
ans=max(ans,(ll)f1[i]*f2[i]);
}
cout<<ans;
return 0;
}
最后的思路转换很重要,要不然就卡这了。