题目描述
小A打算开始炼NOI元丹(什么鬼),据说吃了可以提高NOI时的成绩。
是这么练的。元丹有三种元核,’N’,’O’,’I’。现有很多个这样原核,按顺序排成一行。炼元丹时,从左往右分别挑出’N’,’O’,’I’三个原核吞下。
现在他关心,有几种服用方式……且慢!
他觉得服用方式太少,以至于不能成仙。所以他可以通过某个途径,得到’N’,’O’,’I’的三种原核中的任意一个,至于哪一种由他决定。然后他将获得这个原核的插入到这一排原核中的任意位置(包括最前最后)。
现在你要知道,新的元核序列中能有多少种’N’,’O’,’I’的取出方式。子串的字母并不要求连续。 输入输出格式 输入格式:
第一行,一个整数N,表示字符串的长度。
第二行,一行字符串,里面只有只有’N’,’O’,’I’三种字母。
输出格式:
表示出最多可以提炼出来的NOI元丹的方案种数。
正着扫一遍求出f[i][0/1/2]表示前i个,’N’ ‘NO’ ‘NOI’的个数,
倒着扫一遍求出g[i][0/1/2]表示i..n,’I’ ‘OI’ ‘NOI’的个数。
N一定加在开头,I一定加在末尾,枚举O的位置,总复杂度O(n)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define LL long long
LL f[100010][3],g[100010][3];
int a[100010],n;
char s[100010];
int main()
{
int i,j,k,p,q,x,y,z;
LL ans;
scanf("%d",&n);
scanf("%s",s+1);
for (i=1;i<=n;i++)
if (s[i]=='O') a[i]=1;
else if (s[i]=='I') a[i]=2;
for (i=1;i<=n;i++)
{
f[i][0]=f[i-1][0];
f[i][1]=f[i-1][1];
f[i][2]=f[i-1][2];
if (!a[i]) f[i][0]++;
if (a[i]==1) f[i][1]+=f[i-1][0];
if (a[i]==2) f[i][2]+=f[i-1][1];
}
for (i=n;i;i--)
{
g[i][0]=g[i+1][0];
g[i][1]=g[i+1][1];
g[i][2]=g[i+1][2];
if (!a[i]) g[i][2]+=g[i+1][1];
if (a[i]==1) g[i][1]+=g[i+1][0];
if (a[i]==2) g[i][0]++;
}
ans=max(f[n][2]+f[n][1],f[n][2]+g[1][1]);
for (i=1;i<n;i++)
ans=max(ans,f[n][2]+f[i][0]*g[i+1][0]);
cout<<ans<<endl;
}