ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[NOIP2020]字符串匹配 题解

2022-08-03 21:34:55  阅读:173  来源: 互联网

标签:pre AB QAQ limits 题解 sum 字符串 NOIP2020 sim


传送门QAQ

Preface

怎么题解里全是扩展 KMP 啊,好像就我不会这个东西QAQ。

只能写写大佬们都看不上的哈希+调和了>_<

Analysis

令 \(N=| S|\)。

首先发现,枚举 \(C\) 再判断前缀消耗的时间很多,这样行不通。

转向考虑枚举 \(AB\),得出所有的 \((AB)^i\),不难发现可以用哈希+调和做到 \(O(N\ln N)\)。

现在考虑 \(f(A)\le f(C)\) 的限制。

设 \(pre(i)=f(S_{1\sim i}),suf(i)=f(S_{i+1\sim n})\)。

当前枚举到 \(AB\) 的长度为 \(x\),则 \(AB\) 对答案的贡献为 \(\sum\limits_{i}\sum\limits_{j=1}^x [pre(j)\le suf(x\times i+1)]\)。

预处理出 \(pre(1\sim n),suf(1\sim n)\),用树状数组维护,时间复杂度为 \(O(TN\ln N\log N)\)。

虽然常数小,但只有 \(84\texttt{pts}\)。

仔细地思考一下,这个东西真的必须要用树状数组维护吗?

设 \(sum(i,j)= \sum\limits_{k=1}^i [pre(i)\le j]\),则 \(AB\) 的贡献变为 \(\sum\limits_{i}sum(x,x\times i+1)\)。

而 \(sum\) 数组显然可以用前缀和维护。

时间复杂度 \(O(T(N\ln N+N\times 26))\),足以通过。

Code

本地是 C++17,结果 hash 给我爆关键字了QAQ。

// Problem: #3387. 「NOIP2020」字符串匹配
// Contest: LibreOJ
// URL: https://loj.ac/p/3387
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
const int maxn = (1 << 20) + 5;
const int maxm = 30;
const int p = 131;
typedef long long ll;
char s[maxn];
int n,sum[maxn][maxm],Hash[maxn],pw[maxn],suf[maxn];
ll ans;
void work() {
	scanf("%s",s + 1);
	n = strlen(s + 1);
	Hash[0] = 0;
	pw[0] = 1;
	int cnt[maxm] = {0};
	int cur = 0;
	//calc pre
	for(int i = 1;i <= n;++ i) {
		for(int j = 0;j < maxm;++ j)sum[i][j] = 0;
		pw[i] = pw[i - 1] * p;
		Hash[i] = Hash[i - 1] * p + s[i];
		if(cnt[s[i] - 'a'] & 1)-- cur;
		else ++ cur;
		++ cnt[s[i] - 'a'];
		sum[i][cur] = 1;
	}
	for(int i = 1;i <= n;++ i) {
		for(int j = 1;j <= 26;++ j) {
			sum[i][j] += sum[i][j - 1];
		}
		for(int j = 0;j <= 26;++ j) {
			sum[i][j] += sum[i - 1][j];
		}
	}
	//calc suffix
	memset(cnt , 0 , sizeof(cnt));
	suf[n + 1] = 0;
	for(int i = n;i;-- i) {
		if(cnt[s[i] - 'a'] & 1)suf[i] = suf[i + 1] - 1;
		else suf[i] = suf[i + 1] + 1;
		++ cnt[s[i] - 'a'];
	}
	ans = 0;
	for(int i = 2;i < n;++ i) {
		for(int j = i;j < n;j += i) {
			if(Hash[i] != Hash[j] - Hash[j - i] * pw[i])break ;
			ans += sum[i - 1][suf[j + 1]];
		}
	}
	printf("%lld\n",ans);
	return ;
}
int main() {
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	int T;
	scanf("%d",&T);
	while(T --)work();
	return 0;
}

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

(回看当年赛场上瞎搞的 \(48\texttt{pts}\) 暴力感慨万千qwq)

标签:pre,AB,QAQ,limits,题解,sum,字符串,NOIP2020,sim
来源: https://www.cnblogs.com/663B/p/16548793.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有