[NOIP2020] 字符串匹配

[NOIP2020] 字符串匹配

这真的是个蓝天吗?为什么它的前缀知识是个紫题....
算了,不管那么多了....
首先要有一个意识就是看到循环的时候,可以多向KMP的方向去靠近...(别问我为什么会知道..)
首先任何题都不是一下子想到某个算法,然后根据算法进行变形,靠近当前这个题。而应该是反过来的,我们先观察当前题有什么性质,根据它的性质或是我们需要解决的问题来确定算法之后解决。
首先看这个题\(F(s)\)的定义就很奇怪,奇数次的字符的数量。首先肯定是要将\((AB)^i\)\(C\)是分开的。考虑某种情况下,我们找到了一个划分方式\((AB)^kC\),观察他有什么性质,容易发现k的奇偶性相同时,\(F(C)\)的值是一样的。因为当k每次增加2时,C所在的集合就会减少两个一样的区间。显然这两个一样的区间是对\(F(C)\)是不构成影响的。
那我们接下来的思路就有了,枚举循环节\(AB\),然后找出它最大的循环节。之后考虑\(F(C)\)即可。
循环节最大次数可以使用扩展KMP的z函数解决。F(C)可以预处理后缀的奇数次字符的数量解决。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1100000;
int T,nex[N],n,f[N],cnt[30],c[30];//nex[i]表示[i-n]的最长公共前缀的长度。
char s[N];
inline void get_next()
{
    nex[1]=n;
    int p0=2,p=1;
    while(p+1<=n&&s[p]==s[p+1]) ++p;
    nex[2]=p-1;
    for(int i=3;i<=n;++i)
    {
        if(nex[i-p0+1]+i-1<p) nex[i]=nex[i-p0+1];
        else
        {
            int now=p-i+1;
            now=max(now,0);
            while(now+i<=n&&s[now+1]==s[now+i]) ++now;
            p=i+now-1;p0=i;
            nex[i]=now;
        }
    }
}
inline void get_f()
{
    memset(cnt,0,sizeof(cnt));
    int ans=0;
    for(int i=n;i>=1;--i)
    {
        int id=s[i]-'a';
        cnt[id]++;
        if(cnt[id]%2==1) ++ans;
        else ans--;
        f[i]=ans;
    }
}
inline void add(int x,int v)
{
    x++;
    for(;x<=26;x+=(x&-x)) c[x]+=v; 
}
inline int ask(int x)
{
    x++;
    int ans=0;
    for(;x;x-=(x&-x)) ans+=c[x];
    return ans;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",s+1);
        n=strlen(s+1);
        get_next();get_f();
        memset(cnt,0,sizeof(cnt));
        memset(c,0,sizeof(c));
        int t=1;ll ans=0;
        cnt[s[1]-'a']++;
        add(t,1);
        for(int i=2;i<n;++i)//枚举每一个循环节
        {
            int k=nex[i+1]/i+1;//计算最大的循环节的次数。
            if(i*k==n) --k;
            if(k==1) ans+=ask(f[i+1]);
            else 
            {
                int j=k/2+k%2,l=k-j;//奇偶的数量
                if(k%2==1) ans+=ask(f[i*k+1])*j+ask(f[i*(k-1)+1])*l;
                else ans+=ask(f[i*k+1])*l+ask(f[i*(k-1)+1])*j;
            }
            int id=s[i]-'a';
            cnt[id]++;
            if(cnt[id]%2==1) t++;
            else t--;
            add(t,1);
            // cout<<i<<' '<<ans<<endl;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2022-03-02 13:25  逆天峰  阅读(58)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//