NOIP 2020 字符串匹配
首先观察,不难想出一个暴力。
枚举 AB 的右端点,由于 ABABABAB 一定是从第一个字符开始的,C 一定是从右端点到后面结束,那么我们就可以直接再枚举循环节长度以及 AB 中间的断点然后判断即可。
然后发现这个里面有很多地方是不优的,比如我们重复枚举循环节和断点的部分有很多地方重复算了。
我们首先加一个 hash 来判断两个区间内是不是重复的。
这样我们就可以改成枚举循环节长度,然后枚举循环个数来判断,这样我们只要判断开头一个和末尾一个循环节是不是一样就好了,如果不一样,当前循环节长度就可以直接不往后找了,因为前面的都不符合,后面的符合也没用。
然后考虑如何满足 \(F(a)<F(c)\),我们用一个 \(sum1[i]\) 维护 \(1\sim i\) 出现字符数量满足 \(\le j\) 的位置的数量,\(sum2[i]\) 表示 \(i\sim len\) 出现奇数次的字符的个数。
看一下代码:
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;
}
}
可以看到上面是处理的前 \(i-1\) 个字符的出现奇数次的情况,也就是 \(val\),后面紧接着把 \(sum1\) 更新了,把后面大于等于 \(val\) 的都给加了 \(1\),这表示,如果从 \(i-1\) 断开的话,那么对所有 \(F(C)\ge val\) 的 C 串来说都是可以的,因为当前串 A 串最长是 \(1\sim i-1\) 所以才处理到 \(i-1\) 后面调用 \(sum1\) 来统计答案。
/*
* @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;
}
本文来自博客园,作者:北烛青澜,转载请注明原文链接:https://www.cnblogs.com/Multitree/articles/17794507.html
The heart is higher than the sky, and life is thinner than paper.