zoukankan      html  css  js  c++  java
  • 题解-洛谷P7114 字符串匹配

    题面

    洛谷P7114 字符串匹配

    (T) 组测试数据。给定字符串 (S),问有多少不同的非空字符串 (A)(B)(C) 满足 (S=ABABAB...ABC)(A) 中出现奇数次的字符数不多于 (C)

    数据范围:(1le Tle 5)(1le |S|le 2^{20})


    这估计是我场上唯一做出来的题目了,NOIP2020 游记 也不放洛谷博客里了。


    提供一个 (Theta(n)) 的做法,下标从 (0) 开始。

    求出 (S)Z 数组,(ze_i) 表示满足 (s[0,ze_i)=s[i,i+ze_i)) 的最大值。

    根据 (s[0,ze_i)=s[i,i+ze_i)) 可以推出 (s[i,ze_i)=s[2i,i+ze_i))

    所以可以枚举 (AB) 的长度 (i),易得这个串在前缀重复了 (lfloorfrac{ze_i}{i} floor+1) 次。

    然后解决关于出现奇数次的字符的限制。

    维护一个树状数组,表示每个 (j<i) 的前缀出现奇数次字符数的集合。

    计算前缀出现奇数次字符数可以 (Theta(1)),值域为字符集的树状数组时间复杂度也是 (Theta(1)) 的。

    考虑对于当前的所有 (ABAB...AB) 对应的 (C) 中出现奇数次字符数:只有两种可能。

    一种是当前后缀出现奇数次字符数,一种是全局出现奇数次字符数。先预处理后者,前者可以 (Theta(1)) 维护。

    因为 (ABAB) 中出现奇数次字符数肯定为 (0),所以对于 (lfloorfrac {lfloorfrac{ze_i}{i} floor+1}{2} floor) 的肯定是全局次数,(lceilfrac {lfloorfrac{ze_i}{i} floor+1}{2} ceil) 的肯定是后缀次数。

    然后对于两种情况,答案加上树状数组查询前缀满足条件的 (A) 的数量乘以出现次数即可。

    所以总时间复杂度 (Theta(n))


    代码

    回家手敲的。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef double db;
    #define x first
    #define y second
    #define bg begin()
    #define ed end()
    #define pb push_back
    #define mp make_pair
    #define sz(a) int((a).size())
    #define R(i,n) for(int i(0);i<(n);++i)
    #define L(i,n) for(int i((n)-1);i>=0;--i)
    const int iinf=0x3f3f3f3f;
    const ll linf=0x3f3f3f3f3f3f3f3f;
    
    //Data
    const int N=1<<20,C=26;
    int n,all,last,now,tot[C],cnt[C];
    string str;
    
    //FenwickTree
    int fen[C|1];
    void add(int i,int v){for(;i<C+1;i|=i+1) fen[i]+=v;}
    int sum(int i,int v=0){for(;i>=0;(i&=i+1)--) v+=fen[i]; return v;}
    
    //Zebra
    int ze[N];
    void zebra(){
        int l=0;
        R(i,n) ze[i]=0;
        R(i,n)if(i){
            if(l+ze[l]>i) ze[i]=min(l+ze[l]-i,ze[i-l]);
            while(i+ze[i]<n&&str[ze[i]]==str[i+ze[i]]) ze[i]++;
            if(i+ze[i]>l+ze[l]) l=i;
        }
    }
    
    //Main
    void Main(){
        cin>>str,n=sz(str),zebra();
        R(i,n)if(i+ze[i]==n) ze[i]--;
        ll ns=all=last=now=fen[C]=0;
        R(c,C) tot[c]=cnt[c]=fen[c]=0;
        R(i,n) tot[str[i]-'a']++;
        R(c,C) all+=(tot[c]&1); last=all;
        R(i,n){
            if(tot[str[i]-'a']&1) last--; else last++;
            if(cnt[str[i]-'a']&1) now--; else now++;
            tot[str[i]-'a']--,cnt[str[i]-'a']++;
            if(i&&i<n-1){
                int t=ze[i+1]/(i+1)+1;
                // cout<<"i="<<i<<" t="<<t<<'
    ';
                ns+=1ll*(t/2)*sum(all)+1ll*(t-t/2)*sum(last);
            }
            // cout<<"now="<<now<<" last="<<last<<" all="<<all<<'
    ';
            add(now,1);
        }
        cout<<ns<<'
    ';
    }
    int main(){
        //freopen("string.in","r",stdin);
        //freopen("string.out","w",stdout);
        ios::sync_with_stdio(0);
        cin.tie(0),cout.tie(0);
        int T; cin>>T;
        while(T--) Main();
        return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    Vue过滤器使用
    vue基础
    lesson
    lesson
    lesson
    lesson
    rm 命令详解
    alias 命令详解
    cd 命令详解
    cut 命令详解
  • 原文地址:https://www.cnblogs.com/George1123/p/14092005.html
Copyright © 2011-2022 走看看