首页 > 其他分享 >P7114 [NOIP2020] 字符串匹配 解题报告

P7114 [NOIP2020] 字符串匹配 解题报告

时间:2023-01-09 14:12:08浏览次数:49  
标签:10 return P7114 int leq while 解题 ans NOIP2020

~~NOIP的蓝题果然还是好难啊啊啊啊~~

前言:

作为一道 NOIP 的真题, 这道题放在 T2 难度并不是特别大,不过考点还是比较偏的,扩展KMP和树状数组的组合,并且还带有一定的思维难度,估计是当年不少人低分的题目,同时也给了一些人翻盘的机会。总而言之, 是道好题。

Description:
将字符串 $s$ 划分成 $(AB)^iC$的形式,并且满足 $A$ 中出现奇数次的字符数小于等于 $C$ 中出现奇数次的字符数。

Solution:
首先发现 $(AB)^i$ 中的 $AB$ 是一个循环节,因此想到枚举循环节的长度。
由于 $A,B,C$ 的长度均大于等于1,所以循环节长度应为 $2 \leq len \leq n-1$。

画个图发现, 对于长度为 $i$ 的前缀, 它最多循环的次数为 $z_{i+1}/i$ 下取整 $+1$。设循环次数为 $k$, $f(i,j)$表示 $s[i:j]$ 中出现奇数次的字符数。 当 $k$ 为奇数时, $F(C) = f(i+1, n)$, 因此只要找到满足 $f(1, j) \leq f(i+1, n)$ 的 $j$ 的个数即可, 然后用乘法原理统计。当 $k$ 为偶数时, $F(A)$ 必然等于 $0$, $F(C) = f(1, n)$。

实现可以采用数据结构维护,本题最好用树状数组。

时间复杂度 $O(n log 27)$。

CODE

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') {if (c == '-') f = -f; c = getchar();}
    while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}
    return x * f;
}
const int N = (1 << 20) + 10, C = 26;
int n;
char s[N];
int z[N];
inline void getZ() {
    z[1] = n;
    for (int i = 2, l = 0, r = 0; i <= n; ++ i) {
        if (i <= r) z[i] = min(z[i-l+1], r-i+1);
        while (i + z[i] <= n && s[i+z[i]] == s[z[i] + 1]) ++ z[i];
        if (i + z[i] - 1 > r) r = i + z[i] - 1, l = i; 
    }
}
int tot[C+10], cnt[C+10], c[C+10];
inline int lowbit (int x) {return x & -x;}
inline void add(int x, int v) {for (; x <= C+1; x += lowbit(x)) c[x] += v;}
inline int ask (int x) {int res = 0; for (; x > 0; x -= lowbit(x)) res += c[x]; return res;}
int ans, all, lst, now;
signed main () {
    int T = read(); 
    while (T --) {
        scanf("%s", s + 1); n = strlen(s + 1); 
        for (int i = 1; i <= n; ++ i) z[i] = 0; getZ();
        for (int i = 1; i <= n; ++ i) if (i + z[i] == n + 1) z[i] --;
        for (int i = 1; i <= C+1; ++ i) tot[i] = cnt[i] = c[i] = 0;
        ans = all = lst = now = 0;
        for (int i = 1; i <= n; ++ i) tot[s[i]-'a'+1] ++;
        for (int i = 1; i <= C; ++ i) all += (tot[i] & 1); lst = all;
        for (int i = 1; i <= n; ++ i) {
            if (tot[s[i]-'a'+1] & 1) lst --; else lst ++;
            if (cnt[s[i]-'a'+1] & 1) now --; else now ++;
            tot[s[i]-'a'+1] --; cnt[s[i]-'a'+1] ++;
            if (i > 1 && i < n) {
                int t = z[i+1] / i + 1;
                ans += (t/2) * ask(all+1) + (t - t/2) * ask(lst+1);
            }
            add(now+1, 1);
        }
        printf("%lld\n", ans);
     }
    return 0;
}
View Code

 

标签:10,return,P7114,int,leq,while,解题,ans,NOIP2020
From: https://www.cnblogs.com/CikL1099/p/17036884.html

相关文章

  • AtCoder Beginner Conest 284 解题报告
    AtCoderBeginnerConest284解题报告\(\text{ByDaiRuiChen007}\)\(\text{ContestLink}\)A.SequenceofStrings模拟,时间复杂度\(\Theta(n)\)#include<bits/stdc......
  • 解题报告目录
    解怪题选择性报告(22.6.5~)关键词:低配版解题报告。22.10.10T3mountains关键词:隐藏。22.10.9T4种树关键词:隐藏。图论上的状压DP计数问题关键词:如题。22......
  • CF 1770 解题报告
    CF1770.GoodBye2022,2023isNear晚上十点三十五开打,十一点多就睡觉了,只做了A、C题,其他题都是VP的。感觉质量很高,签到题A题都卡了我一会。A.KoxiaandWhiteboard......
  • LOJ 数列分块入门 9 题解题报告
    LOJ数列分块入门9题解题报告\(\text{ByDaiRuiChen007}\)I.数列分块入门1题目大意\(\text{Link}\)维护一个长度为\(n\)的序列,支持区间加,单点查值思路分析简......
  • 「解题报告」CF755G PolandBall and Many Other Balls
    互测搬的题。教练整的PDFLaTeX全炸了,我在这里再发一遍。(虽然也不会有人看)题目来源:CF755G\(5pts\)做法我会爆搜!直接DFS枚举选择方案。\(20pts\)做法我会DP!设......
  • 解题报告—Dynamite
    Dynamite给一棵树,树上有一些关键节点,要求你选\(m\)个点,第\(i\)个关键节点到这些点中每个点距离的最小值记为\(dis_i\),记这全部\(dis\)的最大值为\(K\),现在要使\(......
  • 解题报告-跳跳棋
    跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有三颗棋子,分别在\(a,b,c\)这三个位置。我们要通过......
  • CTT2022 解题报告
    因为摇奖想看博客,所以这篇文章发出来了!CTT2022解题报告场切题数在Day4突破了0,可喜可贺!D1T1区间计数一个简单的想法是计算【有多少个区间满足它和某个比它更左的区......
  • 事件循环机制(面试快速解题技巧)
    目录事件循环机制同步与异步微任务与宏任务(异步事件)任务执行顺序最终总结事件循环机制同步与异步我们先思考两个问题,如下:为什么会存在同步和异步的概念?我们的JavaScri......
  • CSP2022 解题报告
    JT1乘方\(a=1\),答案为\(1\)。\(a>1\),答案很容易就超过\(10^9\),直接暴力计算即可。如果在做乘法时进行判断,则可以不开longlong。JT2解密\[p+q=n-e\timesd+2......