NOI元丹
【题目描述】
小A打算开始炼NOI元丹(什么鬼),据说吃了可以提高NOI时的成绩。
是这么练的。元丹有三种元核,’N’,’O’,’I’。现有很多个这样原核,按顺序排成一行。炼元丹时,从左往右分别挑出’N’,’O’,’I’三个原核吞下。
现在他关心,有几种服用方式……且慢!
他觉得服用方式太少,以至于不能成仙。所以他可以通过某个途径,得到’N’,’O’,’I’的三种原核中的任意一个,至于哪一种由他决定。然后他将获得这个原核的插入到这一排原核中的任意位置(包括最前最后)。
现在你要知道,新的元核序列中能有多少种’N’,’O’,’I’的取出方式。子串的字母并不要求连续。
【输入格式】
第一行,一个整数N,表示字符串的长度。
第二行,一行字符串,里面只有只有’N’,’O’,’I’三种字母。
【输出格式】
表示出最多可以提炼出来的NOI元丹的方案种数。
【样例输入】
5
NOIOI
【样例输出】
6
【分析】
如果不能插入新的字母就太简单了,可是这题要插入新的字母。
如果要插入N,那么这个N一定插在序列最前面,这个画个图就能证明。
如果要插入I,那么这个I一定插在序列最后面,与上同理。
如果要插入O,那么设leftn[i]表示第i个字母左边N的个数,righti[i]表示第i个字母右边I的个数,枚举使leftn[x]*right[x]最大的x即可。
var
i,n,p:longint;
leftn,righti:array[0..100001]of qword;
ans,t,tt:qword;
s,s1:ansistring;
function max(x,y:qword):qword;
begin
if x>y then exit(x) else exit(y);
end;
function search(st:ansistring):qword;
var
f:array[1..3]of qword;
ch:char;
i:longint;
begin
fillchar(f,sizeof(f),0);
for i:=1 to n+1 do begin
ch:=st[i];
if ch='N' then f[1]:=f[1]+1;
if ch='O' then f[2]:=f[2]+f[1];
if ch='I' then f[3]:=f[3]+f[2];
end;
exit(f[3]);
end;
begin
ans:=0;
readln(n);
readln(s);
n:=length(s);
s1:='N'+s;
ans:=max(ans,search(s1));
s1:=s+'I';
ans:=max(ans,search(s1));
s1:=s;
leftn[0]:=0;
for i:=1 to n do begin
leftn[i]:=leftn[i-1];
if s[i]='N' then inc(leftn[i]);
end;
righti[n+1]:=0;
for i:=n downto 1 do begin
righti[i]:=righti[i+1];
if s[i]='I' then inc(righti[i]);
end;
t:=0;
for i:=1 to n do begin
tt:=leftn[i]*righti[i];
if tt>t then begin t:=tt;p:=i; end;
end;
s1:=s;
insert('O',s1,p+1);
ans:=max(ans,search(s1));
write(ans);
end.