あいさか たいがblogAisaka_Taiga的博客
//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

NOIP 2020 字符串匹配

NOIP 2020 字符串匹配

首先观察,不难想出一个暴力。

枚举 AB 的右端点,由于 ABABABAB 一定是从第一个字符开始的,C 一定是从右端点到后面结束,那么我们就可以直接再枚举循环节长度以及 AB 中间的断点然后判断即可。

然后发现这个里面有很多地方是不优的,比如我们重复枚举循环节和断点的部分有很多地方重复算了。

我们首先加一个 hash 来判断两个区间内是不是重复的。

这样我们就可以改成枚举循环节长度,然后枚举循环个数来判断,这样我们只要判断开头一个和末尾一个循环节是不是一样就好了,如果不一样,当前循环节长度就可以直接不往后找了,因为前面的都不符合,后面的符合也没用。

然后考虑如何满足 F(a)<F(c),我们用一个 sum1[i] 维护 1i 出现字符数量满足 j 的位置的数量,sum2[i] 表示 ilen 出现奇数次的字符的个数。

看一下代码:

Copy
for(int i = 2; i < len; i ++) { ok[a[i - 1] - 'a' + 1] ++; if(ok[a[i - 1] - 'a' + 1] & 1) val ++; else val --; for(int j = val; j <= 26; j ++) sum1[j] ++; for(int j = 1; i * j < len; j ++) { int res = i * j + 1; if(check(1, i, i * (j - 1) + 1, i * j)) ans += sum1[sum2[res]]; else break; } }

可以看到上面是处理的前 i1 个字符的出现奇数次的情况,也就是 val,后面紧接着把 sum1 更新了,把后面大于等于 val 的都给加了 1,这表示,如果从 i1 断开的话,那么对所有 F(C)val 的 C 串来说都是可以的,因为当前串 A 串最长是 1i1 所以才处理到 i1 后面调用 sum1 来统计答案。

Copy
/* * @Author: Aisaka_Taiga * @Date: 2023-10-28 16:49:24 * @LastEditTime: 2023-10-28 19:09:43 * @LastEditors: Aisaka_Taiga * @FilePath: \Desktop\P7114.cpp * The heart is higher than the sky, and life is thinner than paper. */ #include <bits/stdc++.h> #define ull unsigned long long #define int long long #define N 2000100 using namespace std; inline int read() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();} while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); return x * f; } const int base = 131; int T, len, ans, sum1[30], sum2[N], ok[30], val; ull H[N], P[N]; char a[N]; inline int check(int l1, int r1, int l2, int r2) { ull h1 = (H[r1] - H[l1 - 1] * P[r1 - l1 + 1]); ull h2 = (H[r2] - H[l2 - 1] * P[r2 - l2 + 1]); if(h1 == h2) return 1; else return 0; } inline void work() { scanf("%s", a + 1); len = strlen(a + 1); ans = val = 0; for(int i = 1; i <= 26; i ++) ok[i] = 0; for(int i = 1; i <= len; i ++) H[i] = H[i - 1] * base + a[i]; for(int i = len; i >= 1; i --)//处理sum2表示 i-n出现奇数次的字符的数量 { ok[(a[i] - 'a') + 1] ++; if(ok[a[i] - 'a' + 1] & 1) val ++; else val --; sum2[i] = val;//当前1-i出现奇数次字符的数量 } val = 0; memset(sum1, 0, sizeof sum1);//清空 for(int i = 1; i <= 26; i ++) ok[i] = 0; for(int i = 2; i < len; i ++)//枚举AB的长度 { ok[a[i - 1] - 'a' + 1] ++; if(ok[a[i - 1] - 'a' + 1] & 1) val ++; else val --; for(int j = val; j <= 26; j ++) sum1[j] ++;//后面满足 1-i奇数次的字符数量小于等于j的地方数量 for(int j = 1; i * j < len; j ++)//枚举是多少倍 { int res = i * j + 1;//C的开头 // if(check(1, i, i * (j - 1) + 1, i * j)) // cout << "SASND: " << sum1[sum2[res]] << endl; if(check(1, i, i * (j - 1) + 1, i * j)) ans += sum1[sum2[res]];//统计答案,sum2[res]是C中出现奇数次的字符个数 else break;//不合法就直接退出 } } // for(int i = 1; i <= len; i ++) // cout << sum1[i] << " "; // cout << endl; cout << ans << endl; return ; } signed main() { T = read(); P[0] = 1; for(int i = 1; i <= N - 200; i ++) P[i] = P[i - 1] * base; while(T --) work(); return 0; }
posted @   北烛青澜  阅读(70)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示