【做题】BZOJ2534 L-gap字符串——调和级数

题意:给出一个字符串,问其中有多少个子串恰好为uvu的形式。其中,u非空,v的长度恰好为l

n5×104

我们设两个后缀的起点分别为a,b(a<b),那么,它们能贡献一个合法的子串当且仅当lcp(a,b)+lba。这样,我们得到了O(n2)的暴力。

由于lcp(a,b)ba没有什么关系,所以我们考虑枚举ba。设ba=s。那么,答案中u的长度就要满足|u|sl。因此,我们可以忽视长度小于sl的字符串。

考虑这样一种做法:枚举sl,并且把字符串分成nsl段,于是产生O(nsl)个端点。我们发现,所有满足|u|sl的子串u都至少覆盖一个端点。因此,我们枚举所有端点,并计算所有覆盖它的子串对答案的贡献。为了避免重复计算,我们只在一个子串覆盖的最右边的端点计算它的贡献。

现在,我们已有一个端点p,那么另一个u的起点就在p+s。那么,只要求出lcp(p,p+s)lcs(p,p+s),我们就能得出所有以p为其覆盖的最后一个端点的可能的u的数量。

因为Hn=lnn+O(1),所以如果使用O(1)的lcs和lcp,时间复杂度是O(nlogn)的。(但博主写了O(nlog2n)

Copy
#include <bits/stdc++.h> using namespace std; const int N = 80010, BAS = 233; typedef unsigned long long ull; ull has[N],mul[N]; long long ans; int n,l; char s[N]; ull gethas(int a,int b) { return has[b] - has[a-1] * mul[b-a+1]; } bool check(int a,int b,int c,int d) { return gethas(a,b) == gethas(c,d); } int lcs(int a,int b) { int l = 0, r = min(a,b), ret = 0, mid; while (l <= r) { mid = (l + r) >> 1; if (check(a-mid+1,a,b-mid+1,b)) l = mid + 1, ret = mid; else r = mid - 1; } return ret; } int lcp(int a,int b) { int l = 0, r = min(n - a + 1, n - b + 1), ret = 0, mid; while (l <= r) { mid = (l + r) >> 1; if (check(a,a+mid-1,b,b+mid-1)) l = mid + 1, ret = mid; else r = mid - 1; } return ret; } int main() { scanf("%d",&l); scanf("%s",s+1); n = strlen(s+1); for (int i = 1 ; i <= n ; ++ i) has[i] = has[i-1] * BAS + s[i]; mul[0] = 1; for (int i = 1 ; i <= n ; ++ i) mul[i] = mul[i-1] * BAS; for (int s = 1 ; s <= n ; ++ s) { for (int i = 1, j = 1 + s + l, a, b ; j <= n ; i += s, j += s) { a = lcs(i,j); b = lcp(i,j); if (b > s) continue; ans += max(0,a + b - s); } } printf("%lld\n",ans); return 0; }

小结:一些基本套路都不会……字符串能力有待提高。

posted @   莫名其妙的aaa  阅读(183)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示