胡策 zhxP97 a

【问题描述】
你是能看到第一题的 friends 呢。 ——hja
何大爷对字符串十分有研究,于是天天出字符串题虐杀 zhx。 何大爷今天为字符串定义了新的权值计算方法。一个字符串由小写字母组成,字符串的权值 被定义为其中出现次数最多的字符的次数减去出现次数最少的字符的次数。(注意,在讨论出现最少的字符的时候,该字符必须至少出现一次)现在何大爷给 你一个字符串,何大爷想知道这个字符串的所有子串中权值最大的权值是多少?
【输入格式】
第一行一个整数n,代表字符串的长度。 接下来一行n个小写字母,代表该字符串。
【输出格式】
一行一个整数代表答案。
【样例输入】
10
aabbaaabab
【样例输出】
3
【数据范围与规定】
对于30%的数据, 1 ≤ n ≤ 100。
对于60%的数据, 1 ≤ n≤ 1000。
对于100%的数据, 1 ≤ n ≤ 10^6.

30分:暴力找,开一个数组每次统计统计。
60分:开个二维数组记录下前缀和。也就是用sum[i][j],表示前i位第j个字母出现的次数。枚举l,r,然后对于每个区间,枚举下这26个字母的出现情况。找一下取个max。
复杂度是n^2*26;
代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int maxn=20005;
int shu[maxn],tj[maxn][26];
char s[maxn];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n,ans=0;
    char c;
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        shu[i]=s[i]-'0'-48;
    for(int i=1;i<=n;i++)
    {
        tj[i][shu[i]]++;
        for(int j=1;j<=26;j++)
            tj[i][j]+=tj[i-1][j];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            int maxx=0,minn=1e9;
            for(int k=1;k<=26;k++)
            {
                maxx=max(maxx,tj[i][k]-tj[j][k]);
                if(tj[i][k]-tj[j][k]!=0)
                {
                    minn=min(minn,tj[i][k]-tj[j][k]);                   
                }

            }
            ans=max(ans,maxx-minn);
        }
    }
    printf("%d\n",ans);
    return 0;
}

正解思想:
用一个sum[pos][b]表示1~pos中字母b出现的次数,minn[pos][a][b]表示前pos位a与b个数的差的最小值 ,也就是说前pos位,字母a最多比字母b少多少个,pos[a][b]表示minn[a][b]出现的位置,用last[i]表示每个字符出现的最后的位置。每到每一位看看是不是更少了,去更新差的最小值和出现位置就可以了。因为我们是从1~n有序的遍历,所以对于pos位,我们就可以直接将它滚动了,少开一维记录,这样更优一些。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int maxn=1000005;
const int la=30;
char shu[maxn];
int sum[la],last[la],pos[la][la],minn[la][la];
int main()
{   
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n,ans=0;
    scanf("%d",&n);
    scanf("%s",shu+1);
    for(int i=1;i<=n;i++)
    {
        int s=shu[i]-'a'+1;
        last[s]=i;
        sum[s]++;
        for(int j=1;j<=26;j++)
        {
            int tmp;
            if(sum[j]&&s!=j)    //不能自己和自己比较且保证j这一位已经出现过,因为题目要求最少出现一次
            {
                tmp=sum[s]-sum[j]-minn[s][j];
                if(pos[s][j]==last[j]) tmp--;//如果j最后一次出现是在minn[s][j]最小时,要将这个j算上,如果不算这个,j就没出现过,与题意不符 
                ans=max(ans,tmp);
                tmp=sum[j]-sum[s]-minn[j][s];
                if(pos[j][s]==last[j]) tmp--;
                ans=max(ans,tmp);
            }        
        }
        for(int j=1;j<=26;j++)
        {
            if(sum[s]-sum[j]<minn[s][j]) minn[s][j]=sum[s]-sum[j],pos[s][j]=i;    //更新差的最小值和出现位置 
            if(sum[j]-sum[s]<minn[j][s]) minn[j][s]=sum[j]-sum[s],pos[j][s]=i;
        }
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值