a题解

(a)题解

题目描述

首先考虑(60)分暴力,可以对每一个字母用前缀和维护一下。

然后我们可以得到

[ans=max(sum[a][r]-sum[a][l-1])-min(sum[b][r]-sum[b][l-1]) ]

我们设(a)字符为(max)(b)设为(min)

[ans=(sum[a][r]-sum[a][l-1])-(sum[b][r]-sum[b][l-1]) ]

化简得

[ans=(sum[a][r]-sum[a][l-1])-(sum[b][r]-sum[b][l-1]) ]

[ans=sum[a][r]-sum[b][r]-(sum[a][l-1]-sum[b][l-1]) ]

对于字符的枚举,我们发现不用(26^2)去枚举,因为每次加字符只有当前的字符数量变化,所以我们只需要枚举新加的字符就可以了。

我们发现对于(sum[a][l-1]-sum[b][l-1]),我们并不关心(l-1)是什么,我们只关心它们的差值的最小值,所以我们用(minn[a][b])表示(a)(b)差值的最小值。

对于(sum[a][r])(sum[b][r]),因为(r)是当前枚举的,所以我们直接用(sum[a])(sum[b])来代替。

所以

[ans=max(ans,sum[a]-sum[b]-minn[a][b]) ]

因为区间内(a)或者(b)都不能等于(0),所以我们记录一个(pos[a][b])表示(minnn[a][b])出现的位置,记录一个(last[j])表示(j)这个字符最后出现的位置,如果(pos[a][b]==last[b])证明这段区间里没有(b),所以我们要新加在(last[b])位置的这个(b),也就是更新时答案减一。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100,M=30;
char s[N];
int sum[M],last[M],pos[M][M],minn[M][M];
int n,ans=0;
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    scanf("%s",s+1);
    for (int i=1;i<=n;i++)
    {
        int now=s[i]-'a'+1;
        last[now]=i;
        sum[now]++;
        for (int j=1;j<=26;j++)
        {
            int tmp;
            if (sum[j]&&now!=j)
            {
                tmp=sum[now]-sum[j]-minn[now][j];
                if (pos[now][j]==last[j]) tmp--;
                ans=max(ans,tmp);
                tmp=sum[j]-sum[now]-minn[j][now];
                if (pos[j][now]==last[j]) tmp--;
                ans=max(ans,tmp);
            }
        }
        for (int j=1;j<=26;j++)
        {
            if (sum[now]-sum[j]<minn[now][j])
            {
                minn[now][j]=sum[now]-sum[j];
                pos[now][j]=i;
            }
            if (sum[j]-sum[now]<minn[j][now])
            {
                minn[j][now]=sum[j]-sum[now];
                pos[j][now]=i;
            }
        }
    }
    printf("%d
",ans);
    getchar();getchar();
    return 0;
}
原文地址:https://www.cnblogs.com/last-diary/p/11479150.html