51Nod 1701 最后的机会

1701 最后的机会

给定一字符串S,S非空,由小写字母组成,设v为S中元音字母的个数,c为辅音字母的个数。"a", "e", "i", "o", "u"为元音字母。其余为辅音字母。如果元音字母没有超过辅音字母的2倍,即v≤2c则称S是好的字符串。

现在给定S,找出其中最长的好的子串的长度,以及个数。子串是由原串中连续的几个字符组成的。

样例解释:

在样例一中,最长的子串是他的自身“abo”。其它好的子串是“b”,“ab”,“bo”,但是都不是最长的。

在样例二中,最长的子串是“eis”。其它好的子串是“s”,“is”,但是都不是最长的。

输入

单组测试数据。
共一行字符串S(S非空且长度不大于2*10^5),由小写字母组成。其中"a", "e", "i", "o", "u"为元音字母。其余为辅音字母。

输出

输出两个整数,以空格隔开,分别表示最长的好的子串的长度和以及个数。如果没有好的子串则输出 "No solution" (没有引号)。

两个子串出现的位置不一样则称为是不同的子串。如果同一个子串在不同位置出了多次,则要统计多次。

输入样例

样例一
abo
样例二
oeis

输出样例

输出一
3 1
输出二
3 1

分析

对于符合的任意一段子序列(长度为len),假设其元音字符的个数为sum, 则依题意有sum<=2*(len-sum);

假如求出了元音字符数量的前缀和sum[]. 具体一点,假设这个子序列的左右下标为 i 和 j ,则有:

sum[j]-sum[i-1]<=2*((j-i+1)-(sum[j]-sum[i-1])) , 移项得 3*sum[j]-2*j<=3*sum[i-1]-2*(i-1), 想到可以记录一个

数组存储3*sum[i]-2*i的值。我们要求的最大长度即是满足上式的距离最远的i和j,只需再开一个数组rmin

(右边的最小值),用单调的思想处理一下,枚举i然后二分查找j,更新最大长度。。。

代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
bool ok(char c)
{
    return c=='a'||c=='e'||c=='i'||c=='o'||c=='u';
}
char s[200005];
int ans1,ans2;
int sum[200005];
int T[200005];
int rmin[200005];
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%s",s);

    int n=strlen(s);
    for(int i=1;i<=n;i++)
        if(ok(s[i-1]))sum[i]=sum[i-1]+1;
        else sum[i]=sum[i-1];

    for(int i=1;i<=n;i++)
        T[i]=3*sum[i]-2*i;

    int tmp=0x3f3f3f3f;
    for(int i=n;i>=1;i--)
    {
        tmp=min(tmp,T[i]);
        rmin[i]=tmp;
    }

    for(int i=1;i<=n;i++)
    {
        int v=3*sum[i-1]-2*(i-1);
        int j=upper_bound(rmin,rmin+1+n,v)-rmin; j--;
        if(i<=j){ans1=max(ans1,j-i+1);}
    }

    for(int i=1,j=i+ans1-1;j<=n;i++,j++)
    {
        if(3*sum[j]-3*sum[i-1]<=2*(j-i+1))ans2++;
    }

    if(ans1==0){printf("No solution
");}
    else printf("%d %d
",ans1,ans2);

    return 0;
}
原文地址:https://www.cnblogs.com/linruier/p/10344717.html