[NOIP2020]字符串匹配
壹、题目描述 ¶
贰、关于题解 ¶
一个质朴的想法是枚举 \(|AB|\),然后考虑他们的一倍、两倍、三倍......基础复杂度显然是 \(\mathcal O(n\ln n)\) 的,不过问题是,内部需要多少的复杂度才可以尽可能地快?
先考虑暴力一点的,我们可以用 \(\mathcal O(26)\) 的复杂度计算出 \(f(s[|AB|+1:n])\),然后看看我们初始的 \([1,|AB|]\) 中有多少个前缀的 \(f\) 比它大,显然,这样做复杂度为 \(\mathcal O(n\ln n\times |\Sigma|\times T)\),此时能够有 \(84pts\),不过还可以进一步优化。
我们可以预处理出每一个前缀、每一个后缀的 \(f\),毫无疑问,这可以 \(\mathcal O(n)\) 做到,然后,再使用上述做法,即可将复杂度降低至 \(\mathcal O(T(n\ln n)+|\Sigma|n)\) 了,似乎有点卡,但是还是过掉了,曾经的我到底有多脑瘫啊......
至于如何判断循环节,很简单,直接哈希或者 \(\rm KMP\) 或者 \(\rm exKMP\) 就可以了,代码里面用的 \(\rm KMP\),因为它多快好省,且不会被卡。
叁、参考代码 ¶
#include <bits/stdc++.h>
using namespace std;
#define USING_STDIN
//#define NDEBUG
//#define NCHECK
#include <cassert>
namespace Elaina {
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
template<class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }
#ifndef USING_STDIN
inline char qkgetc() {
# define BUFFERSIZE 1 << 17
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif
template<class T> inline T readret(T x) {
x=0; int f = 0; char c;
while((c = CHARRECEI) < '0' || '9' < c) if(c == '-') f = 1;
for(x = (c ^ 48); '0' <= (c = CHARRECEI) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template<class T> inline void readin(T& x) {
x=0; int f = 0; char c;
while((c = CHARRECEI) < '0' || '9' < c) if(c == '-') f = 1;
for(x = (c ^ 48); '0' <= (c = CHARRECEI) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
if(f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
// default enter
template<class T> inline void writln(T x, char s = '\n') {
static int fwri_sta[55], fwri_ed = 0;
if(x < 0) putchar('-'), x = -x;
do fwri_sta[++fwri_ed] = x % 10, x /= 10; while(x);
while(putchar(fwri_sta[fwri_ed--] ^ 48), fwri_ed);
putchar(s);
}
} using namespace Elaina;
const int maxn = 1 << 20;
const int sigma = 26;
int n;
char s[maxn + 5];
inline void input() {
scanf("%s", s + 1);
n = strlen(s + 1);
}
int pre[maxn + 5], suf[maxn + 5];
int buc[sigma + 5];
inline void prelude() {
rep(i, 1, n) pre[i] = suf[i] = 0;
for(int i = 0; i < sigma; ++i) buc[i] = 0;
int cnt = 0;
rep(i, 1, n) {
if(buc[s[i] - 'a']) --cnt;
else ++cnt;
buc[s[i] - 'a'] ^= 1;
pre[i] = cnt;
}
for(int i = 0; i < sigma; ++i) buc[i] = 0;
cnt = 0;
drep(i, n, 1) {
if(buc[s[i] - 'a']) --cnt;
else ++cnt;
buc[s[i] - 'a'] ^= 1;
suf[i] = cnt;
}
}
int nxt[maxn + 5];
inline void getnxt() {
nxt[0] = -1, nxt[1] = 0;
for(int i = 2; i <= n; ++i) {
int len= nxt[i - 1];
while(~len && s[len + 1] != s[i])
len = nxt[len];
nxt[i] = len + 1;
}
}
int cnt[sigma + 5];
inline void solve() {
input();
prelude();
getnxt();
for(int j = 0; j <= sigma; ++j)
cnt[j] = 0;
ll ans = 0;
int mn;
rep(i, 2, n - 1) {
for(int j = pre[i - 1]; j <= sigma; ++j)
++cnt[j];
ans += cnt[suf[i + 1]];
for(int j = i << 1; j < n; j += i) {
mn = j - nxt[j];
if(i % mn != 0) break;
ans += cnt[suf[j + 1]];
}
}
writln(ans);
}
signed main() {
rep(_, 1, readret(1)) solve();
return 0;
}