首页 > 其他分享 >NOIP2020 T2 字符串匹配【题解】

NOIP2020 T2 字符串匹配【题解】

时间:2023-06-16 22:35:15浏览次数:48  
标签:Suf int 题解 T2 Per 次方 MAXN 字符串 NOIP2020

NOIP2020 T2 字符串匹配

首先声明

这篇题解存在大多数让我这种人看懂的废话,如果想要速通,请另寻他解

题目简化

定义字符串乘法为 \(AB\) 为把两个字符串拼起来,定义阶乘 \(A^i\) 表示 \(\prod_{1}^i A\)

再定义 \(F(S)\) 为 \(S\) 中出现奇数次字符的数量

现给定一个字符串 \(S\),求找到 \(S=(AB)^iC\) 的方案数(\(|A,B,C|>0\))满足 \(F(A)\le F(C)\)

浅浅谈一下吧

我们观察这道题的数据范围,发现只允许 \(\mathcal{O}(n\log n)\) 的算法通过,那我们就会有一个思路

枚举 \(C\) 看看前面的合不合法,合法就加

那我们怎么知道这段区间合不合法呢?

容易想到,把 \(AB\) 看成同一个字符串,判断这段区间是否为 \(S^i\) 即可

问题又来了,怎么判断 \(S^i\) 呢?

联想到了,最小周期为 \(i-\pi_i\),记为 \(p\)(使用 \(KMP\) 求解)

那只需要满足 \(p|i\) 就可以满足 \(i\) 可以被若干个 \(p\) 拼成

于是思路就来了,我们枚举 \(i\) 判断当前的 \(|S|^i\) 的周期是否合法

不过还有另外一个限制,即 \(p\) 要能拼成 \(S\) 才行(主人公是 \(S\)),即需要满足 \(p\ |\ |S|\)

那两个都需要判断吗?不需要,因为 \(i\) 本身就是 \(|S|\) 的倍数,代码如下

for (int j = i + i;/*避免出现1次方,会错*/ j < n; j += i) {
	int q = j - N[j];
	if (i % q == 0 && j / q > 1) //保证x次方(x>1) 
	统计答案
}

那怎么统计答案呢?

注意到,我们可以记一个数组 \(Per_i\) 表示当前位置满足奇数字母大于等于 \(i\) 的字符串数量

另外,\(A\) 是个前缀,说明可以直接进行更新,每过一个位置,就把 \(A[0...i]\) 加进合法的答案里

但是,我们需要预处理前缀数组 \(Pre_i\) 和后缀数组 \(Suf_i\),即可完成这道题

注意细节

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 2e6 + 7;

typedef long long ll;

#define Ms(a, k) (memset(a, k, sizeof(a)))

int T;

char S[MAXN];

int n, N[MAXN];

int t[MAXN], Pre[MAXN], Suf[MAXN], Per[MAXN];

signed main () {
	ios :: sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	
	for (cin >> T; T; T --) {
		cin >> S + 1; n = strlen(S + 1);
		
		N[1] = 0; Ms(Pre, 0); Ms(Suf, 0); Ms(Per, 0);
		
		for (int i = 2; i <= n; i ++) {
			int  p = N[i - 1];
			while (p != 0 && S[p + 1] != S[i]) p = N[p];
			if (S[p + 1] == S[i]) p ++; N[i] = p; 
		}
		
		Ms(t, 0);
		for (int i = 1; i <= n; i ++) 
			if ((++ t[S[i] - 'a' + 1]) & 1) Pre[i] = Pre[i - 1] + 1;
			else Pre[i] = Pre[i - 1] - 1; 
		Ms(t, 0);
		for (int i = n; i >= 1; i --)
			if ((++ t[S[i]] - 'a' + 1) & 1) Suf[i] = Suf[i + 1] + 1;
			else Suf[i] = Suf[i + 1] - 1;
		
		ll ans = 0;
		for (int i = 1; i < n;/*C不为空*/ i ++) {
			if (i >= 2) { //B不为空
				ans += (ll)Per[Suf[i + 1]];//1次方 
				for (int j = i + i;/*避免出现1次方,会错*/ j < n; j += i) {
					int q = j - N[j];
					if (i % q == 0 && j / q > 1) //保证x次方(x>1) 
						ans += (ll)Per[Suf[j + 1]];	
				} 
			}
			for (int j = Pre[i]; j <= 30; j ++) Per[j] ++; //A是前缀,将A[1...i]加进去
		}
		cout << ans << '\n';
	}
	return 0;
}

完结撒花✿✿ヽ(°▽°)ノ✿

标签:Suf,int,题解,T2,Per,次方,MAXN,字符串,NOIP2020
From: https://www.cnblogs.com/Phrvth/p/17486616.html

相关文章

  • P1903 [国家集训队] 数颜色 / 维护队列 题解
    一、题目描述:给你一个长度为$n$的序列$a$,你需要进行$m$次操作。$类型\1\:将第\x\个元素的值修改为\v\。$$类型\2\:求区间\l\到\r\中有多少种数字。$数据范围:$1\len,m\le1333333,所有数字\le1\times10^6$ 二、解题思路:带......
  • springboot2 自动装配原理
    springboot自动装配Spring支持两种bean配置方式:XML配置、JavaConfig配置@SpringBootApplication注解我们创建一个springboot项目后,一般要用该注解,然后在springbootApplication.run方法传入标注了该注解的类,这样就可以去加载spring的相关操作@SpringBootApplicationpublic......
  • CF1205C Palindromic Paths 题解
    很好的一道题,思路自然,步骤清晰,结论好猜。唯一的缺点可能只是我赛时没有看到。构造可行解看到题目,也许我们很快就能想出一个做法:每次询问\((i,j,i+1,j)\),每行第一个额外询问\((i,j,i+1,j)\),这样询问总共\(n^2-1\)次即可。当我们怀疑看错题目重新检查时发现了被微软翻译......
  • 「ULSG-1」泡水的铅筒 题解
    题目传送门题目描述一个圆锥放入一个长方体水池中,无水溢出,求长方体液面高度的最大、最小值。解题思路如果这个题只有一个数据点,此数据点只有一组数据,那这就是一道初中填空题()先考虑\(h1\)的最小值。由“铅筒被完全浸没且没有液体溢出水池外”一句可得,若圆锥放入水池后液面高......
  • 「ULSG-1」2048 题解
    题目传送门题目解析玩一次就明白了。传送门解题思路合并从下往上,从左往右,对于每一个非零的数,向上第一个非零数,若与之相等,则此数与找到的数相加,同时分数加上合并后的数,而找到的数清零。若第一个非零数与它不相等,直接停止寻找过程,意为无法合并,等待下落。下落依旧从下往上,从......
  • 「ULSG-1」数字生命 题解
    题目传送门题目描述给定一段长度为\(n\)的序列,找出其中长度为\(m\)的一段子序列,且其中各数字出现次数与给定模板中相对应的次数不相同的数字等于\(k\)。题目解法容易联想到一个用于求固定长度区间最大值的\(O(n)\)算法——「滑动窗口」,此题可借鉴此算法。我们以此题的......
  • 「SiR-1」Checkmate 题解
    题外话:本体题目出自番剧《NOGAMENOLIFE》且题目背景中来吧,游戏开始了。是第一季中男主“空”的口头禅。(强烈推荐观看《NOGAMENOLIFEZERO》)回归正题awaP9355「SiR-1」Checkmate题解题目传送门博客宣传题目解读:在\(n\)行\(m\)列的棋盘中放置多枚棋子,求出其上......
  • 【BZOJ 3156】防御准备 题解
    原题令\(S_{i}=\sum_{j=1}^{i}j\),\(f_{i}\)为处理到第\(i\)个位置放置守卫塔的最小花费。观察题意,容易得到在\((1<=j<=i-1)\)时,有\(f_{i}=min\left\{f_{j}+\sum_{k=j+1}^{i-1}(i-k)+a_{i}\right\}\)①\(f_{i}=min\left\{f_{j}+\sum_{k=j+1}^{i-1}(i-k)\ri......
  • springboot2.4以后的配置
     (10条消息)springboot2.4以后的配置_spring.config.activate.on-profile_眉宇下的小格调的博客-CSDN博客......
  • Linux中-bash: /dev/null: Permission denied问题解决
    云上架构2021年08月06日09:19 ·  阅读682​今天在Centos7上运行如下命令 shell复制代码######添加hdfs用户#####useraddhdfs######切换至hdfs用户#####su-hdfs报如下错误 javascript复制代码-bash:/dev/null:Permissiondenied-bash......