loj3387. 「NOIP2020」字符串匹配

题目链接

大概是我的心理阴影题了。

首先有一个枚举 C 的做法可以拿到 48 分,看起来没什么优化空间,我们转而枚举 AB。

枚举 AB 的长度,可以使用字符串哈希来判断这个前缀的重复次数(假设已经重复了 \(k\) 次,那么我们将前 \(k-1\) 次构成的串整体右移 \(i\) 位,即哈希值乘上 \(base^i\),然后我们就把当前字符串和上一个字符串右对齐了,然后哈希值相减,容易得出当且仅当最后 \(i\) 位和循环节相同时哈希值等于循环节的哈希,用公式表达就是 \(h_{ik}-h_{i(k-1)}base^i=h_i\))。

需要统计满足 \(F(A)\leq F(C)\) 的合法方案数。发现 \(C\) 的取法只能是边角料加上若干循环节,而两个循环节的影响就会完全抵消,那么本质上不同的 \(F(C)\) 取值只有两个,可以预处理后缀和 \(O(1)\) 得到。

然后我们需要查询合法的 \(A\) 的数量,显然 \(A\) 是当前循环节的前缀,于是我们把所有前缀的 \(F(A)\) 上树状数组直接查询即可。

计算贡献的时候分两类讨论,分别查出可行的 \(A\) 的个数,然后乘上当前这种 \(F(C)\) 对应了多少个合法的 \(C\) 的即可。

时间复杂度 \(O(n\ln n+n\log 26)\)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int base=2333,mod=998244353;
int t,n,sum[31],h[1<<20|5],f[1<<20|5][2],qwq;
bool cnt[26];
long long ans;
char s[1<<20|5];
inline int read()
{
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x;
}
void print(long long x)
{
    if(x>=10)
        print(x/10);
    putchar(x%10+'0');
}
inline int lowbit(int x)
{
    return x&-x;
}
inline void update(int x)
{
    for(;x<=27;x+=lowbit(x))
        ++sum[x];
}
inline int query(int x)
{
    int res=0;
    for(;x;x-=lowbit(x))
        res+=sum[x];
    return res;
}
int main()
{
    t=read();
    while(t--)
    {
        scanf("%s",s+1);
        n=strlen(s+1);
        for(register int i=0;i<26;++i)
            cnt[i]=0;
        for(register int i=1;i<=27;++i)
            sum[i]=0;
        f[0][0]=0;
        for(register int i=1;i<=n;++i)
        {
            h[i]=(1ll*h[i-1]*base%mod+s[i]-'a'+1)%mod;
            if(cnt[s[i]-'a'])
                f[i][0]=f[i-1][0]-1;
            else
                f[i][0]=f[i-1][0]+1;
            cnt[s[i]-'a']^=1;
        }
        for(register int i=25;~i;--i)
            cnt[i]=0;
        f[n+1][1]=0;
        for(register int i=n;i;--i)
        {
            if(cnt[s[i]-'a'])
                f[i][1]=f[i+1][1]-1;
            else
                f[i][1]=f[i+1][1]+1;
            cnt[s[i]-'a']^=1;
        }
        update(2);
        qwq=1ll*base*base%mod;
        ans=0;
        for(register int i=2;i<n;++i)
        {
            register int tmp=1;
            for(register int j=i<<1;j<n;j+=i)
                if((h[j]-1ll*h[j-i]*qwq%mod+mod)%mod==h[i])
                    ++tmp;
                else
                    break;
            ans+=1ll*query(f[tmp*i+1][1]+1)*((tmp+1)>>1);
            if(tmp^1)
                ans+=1ll*query(f[(tmp-1)*i+1][1]+1)*(tmp>>1);
            qwq=1ll*qwq*base%mod;
            update(f[i][0]+1);
        }
        print(ans);
        putchar('\n');
    }
    return 0;
}
posted @ 2021-09-23 20:23  绝顶我为峰  阅读(88)  评论(1编辑  收藏  举报