今天颓了一天T T

  这题有两种写法...

  ①预处理出每种字符在原字符串中的位置,枚举两种字符作为最大值和最小值,把这两种字符的坐标归并排序,把最大值设为1,最小值设为-1,求最大子段和。注意因为最小值必须出现一次,所以要记录前缀最小值和次小值,答案只更新最小值出现次数不为0的一个,对于一个字符的出现次数用当前出现次数是否等于前缀最小值出现次数来判断就好了,复杂度O(50N)。

#include<iostream> 
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath> 
#include<algorithm> 
using namespace std;
const int maxn=1000010,inf=1e9;
int n,ans,cnt;
int num[maxn],sum[maxn],last[maxn],pre[maxn],q[maxn];
char s[maxn];
inline void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
    while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
    k*=f;
}
void solve(int x,int y)
{
    int mn1=0,mn2=inf,frt1=0;
    for(int i=1;i<=cnt;i++)
    {
        num[i]=num[i-1]+(s[q[i]]-'a'==y);
        sum[i]=sum[i-1]+(s[q[i]]-'a'==x?1:-1);
        if(num[i]==frt1)mn1=min(mn1,sum[i]);
        else if(sum[i]<mn1)mn2=mn1,mn1=sum[i],frt1=num[i];
        else if(sum[i]<mn2)mn2=sum[i];
        if(num[i]-frt1>0)ans=max(ans,sum[i]-mn1);
        else ans=max(ans,sum[i]-mn2);
    }
}
int main()
{
    read(n);scanf("%s",s+1);
    for(int i=1;i<=n;i++)pre[i]=last[s[i]-'a'],last[s[i]-'a']=i;
    for(int i=0;i<26;i++)
    for(int j=0;j<i;j++)
    if(last[i]&&last[j])
    {
        cnt=0;int l1=last[i],l2=last[j];
        while(l1||l2)
        if(l1>l2)q[++cnt]=l1,l1=pre[l1];
        else q[++cnt]=l2,l2=pre[l2];
        solve(i,j);solve(j,i);
    }
    printf("%d\n",ans);
}
View Code

相关文章:

  • 2022-12-23
  • 2021-08-19
  • 2021-11-27
  • 2021-08-08
  • 2021-07-04
  • 2021-06-28
  • 2022-01-22
  • 2021-11-04
猜你喜欢
  • 2021-09-10
  • 2021-08-01
  • 2022-03-03
  • 2021-09-17
  • 2022-03-10
  • 2021-12-14
  • 2021-07-22
相关资源
相似解决方案