P7114 [NOIP2020] 字符串匹配

链接

代码先行

#include <bits/stdc++.h>
using namespace std;

const int maxx = (1 << 21);
string s;
int pos[maxx][26],pre[maxx][26],cpos[maxx],cpre[maxx];
int cnt[maxx];//辅助统计当前奇数个数为i的个数
int temp,postodd,temp2,postodd2;
int z[maxx];
int T,len;
int l,r;
int i,k;
int n1,n2;
long long ans;

void exkmp()
{
    l = r = 0;
    z[0] = 0;
    for (i = 1; i < len; i++)
    {
        if(r >= i && z[i - l] < r - i + 1)
        {
            z[i] = z[i - l];
        }
        else{
            z[i] = max(0,r - i + 1);
            while( i + z[i] < len && s[ z[i] ] == s[ i + z[i] ] ) z[i]++;
        }
        if(i + z[i] > l + z[l]) l = i,r = i + z[i] - 1;
        //cout << z[i] << " ";
    }
z[0] = len;
}

void prep()
{
    memset(pre,0,sizeof(pre));
    memset(pos,0,sizeof(pos));
    pre[0][ s[0] - 'a' ] ^= 1;
    if(pre[0][ s[0] - 'a' ] == 0)
    {
        cpre[0] = 0;
    }
    else cpre[0] = 1;
    for (i = 1; i < len; i++)
    {
        memcpy(pre[i],pre[i - 1],sizeof(pre[i]));
        pre[i][ s[i] - 'a' ] ^= 1;
        if(pre[i][ s[i] - 'a' ] == 0)
        {
            cpre[i] = cpre[i - 1] - 1;
        }
        else cpre[i] = cpre[i - 1] + 1;
    }

    pos[len - 1][ s[len - 1] - 'a' ] ^= 1;
    if(pos[len - 1][ s[len - 1] - 'a' ] == 0)
    {
        cpos[len - 1] = 0;
    }
    else cpos[len - 1] = 1;
    for (i = len - 2; i >= 0; i--)
    {
        memcpy(pos[i],pos[i + 1],sizeof(pos[i]));
        pos[i][ s[i] - 'a' ] ^= 1;
        if(pos[i][ s[i] - 'a' ] == 0)
        {
            cpos[i] = cpos[i + 1] - 1;
        }
        else cpos[i] = cpos[i + 1] + 1;
    }
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);

    cin >> T;
    while(T--)
    {
        memset(cnt,0,sizeof(cnt));
        ans = 0;
        cin >> s;
        len = s.length();
        exkmp();
        prep();
        postodd = cpos[0];
        postodd2 = cpos[0];
        temp = 0;
        temp2 = 0;
        for (i = 1; i < len; i++)//枚举AB串长度
        {
            k = min(z[i],len - 1 - i) / i + 1;//最大的重复次
            n1 = (k + 1) >> 1,n2 = (k) >> 1;
            

            //处理k为奇数的情况
            if( pos[i - 1][ s[i - 1] - 'a' ] == 1 )//下一步会变成奇数,加入前缀
            {
                temp -= cnt[postodd];//减去前面所有至结尾为止个数为奇数的个数
                postodd--;
            }
            else
            {
                postodd++;
                temp += cnt[postodd];
            }
            ans += 1ll * temp * n1;//所有为奇数的
            cnt[ cpre[i - 1] ]++;
            if(cpre[i - 1] <= postodd) temp++;//B串不能为空串,所以A的末尾必须小与i - 1,所以ans更新在前面,在这里错了好久!!!!!
        
            /*if(pos[i - 1][ s[i - 1] - 'a' ] == 1)//减去一个奇数
            {
                temp2  -= cnt[postodd2];
                postodd2--;
            }
            else//减去一个偶数
            {
                postodd2++;
                temp2 += cnt[postodd2];
            }
            if(cpre[i - 1] <= postodd2) temp2++;*/

            //辅助处理k为偶数的情况,等价于与整个串的奇偶性的匹配
            ans += 1ll * temp2 * n2;//所有为偶数的
            if(cpre[i - 1] <= cpre[len - 1])temp2++;
        }
        cout << ans << endl;
    }
    return 0;
}

思路:

        分析题目,得到目标:枚举AB长度,找到AB串中满足条件的A的个数,注意此时B不为空

        1.用exkmp去判断循环次数

        2.注意到题目中重要的是奇偶性,所以只考虑循环次数的奇偶性,由于 k <= Zi / i​​+1 并且C串不为空,所以ik <= n - 1,得出k范围,分奇偶(n1,n2)

        3.优化复杂度,由于postodd每次只变化1,那么我们记录一下,从i变化到i + 1或者i - 1时,A串满足条件的个数的变化情况,奇数k没什么好说的,偶数k:我们注意到偶数k的时候,C串的奇偶性与整串一致,于是直接用整串的奇偶性即可。

有一个很坑的点,再exkmp里,我先把z[0] 初始化为 len, 然后导致 l + z[l] 很大,l一直不更新,和暴力同分。

posted @ 2022-07-13 19:57  此间无物  阅读(25)  评论(0编辑  收藏  举报