题目链接:https://uoj.ac/problem/581
题目大致意思:
数据规模:T<=5, S长度不超过2^20
题解:可以说是非常阴间了放在NOIP第二题。
我们拆解一下问题,首先我们需要枚举若干个(AB)^i,那么每个(AB)^i 从 (AB)^(i-1) 递推出来,中间一定长度的一段子串是否为与对应长度前缀相同,可以哈希去处理,但是精确做法是exkmp算法。
为什么NOIPT2考EXKMP?可能是时代变了吧。
EXKMP算法可以参考百度百科。作用是算出一个串A的所有后缀与串B的公共最大前缀长度。这里不细说了。
EXKMP预处理了S字符串之后,单次比较就是O(1)的复杂度。
i的大小可以依次枚举,根据筛法可以证明n/2 + n/3 + n/4 + ... 约等于 n ln n - n;所以枚举的复杂度也不高。
最后就是枚举满足F(A)<=F(C)的A的个数,复杂度也需要降到O(1)才可能通过此题。
所以我们从前到后递推每一个A前缀对应的数组,对于每一个A的对应前缀。
把其不同的奇数个数字符的前缀字符串个数(没错,就是A前缀对应的所有前缀数量分量),依次存下来,放在numPrefixA数组中,最多0...26中不同的奇数个数的字符。
C也把所有的后缀对应的奇数个数字符个数记录下来,放在oddNumC数组中。
过程中,CountA与CountC用来辅助存每个字符出现的次数。结合代码理解一下过程。
最后,为了枚举simga(1<=F(A)<=F(C)) 所对应的A的个数,把numPrefixA数组以前缀和形式存下来。
for (uint32_t len = 0; len < S.length() - 1; len++)
for (uint32_t num = 1; num <= 26; num++) {
numPrefixA[len][num] += numPrefixA[len][num - 1];
}
最终,AC的代码如下:
#include "bits/stdc++.h"
using namespace std;
const uint32_t maxN = 1<<20;
int32_t T;
string S;
int32_t AC[maxN + 10];
int32_t exKMP[maxN + 10], numPrefixA[maxN + 10][27], oddNumC[maxN + 10], CountA[255], CountC[255];
void EXKMPS()
{
exKMP[0] = S.length();
int32_t pos = 0, maxSuffixPo = 1;
while (S[pos] == S[pos + 1]) pos++;
exKMP[1] = pos;
for (int32_t i = 2; i < S.length(); i++) {
if (exKMP[i - maxSuffixPo] < exKMP[maxSuffixPo] + maxSuffixPo - i) {
exKMP[i] = exKMP[i - maxSuffixPo];
} else {
int len = exKMP[maxSuffixPo] + maxSuffixPo - i;
if (len < 0) {
len = 0;
}
maxSuffixPo = i;
while (S[i + len] == S[len]) len++;
exKMP[i] = len;
}
}
}
void updateOdd(char ch, int32_t* Count, uint8_t &oddC)
{
Count[ch]++;
if (Count[ch] & 1) {
oddC++;
} else {
oddC--;
}
}
void PrepareAC()
{
uint8_t oddA = 0;
uint8_t oddC = 0;
memset(CountA, 0, 255 * sizeof(int32_t));
memset(CountC, 0, 255 * sizeof(int32_t));
for (uint32_t len = 0; len < S.length(); len++) {
updateOdd(S[S.length() - 1 - len], CountC, oddC);
oddNumC[len] = oddC;
if (len) {
for (int num = 0; num <= 26; num++) {
numPrefixA[len][num] = numPrefixA[len - 1][num];
}
updateOdd(S[len - 1], CountA, oddA);
numPrefixA[len][oddA]++;
}
}
for (uint32_t len = 0; len < S.length() - 1; len++)
for (uint32_t num = 1; num <= 26; num++) {
numPrefixA[len][num] += numPrefixA[len][num - 1];
}
}
int main()
{
cin >> T;
while (T--) {
cin >> S;
PrepareAC();
EXKMPS();
uint64_t ans = 0;
for (uint32_t i = 2; i < S.length(); i++) {
for (uint32_t j = i; (j < S.length()) && (exKMP[j - i] >= i); j+=i) {
ans += numPrefixA[i - 1][oddNumC[S.length() - j - 1]];
}
}
cout << ans << endl;
}
}
这种究极缝合怪codeforces2200的题目
我不知道它存在NOIPT2的意义