[NOIP2020]字符串匹配

壹、题目描述 ¶

传送门 to Luogu.

贰、关于题解 ¶

  一个质朴的想法是枚举 \(|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;
}
posted @ 2021-10-21 21:15  Arextre  阅读(63)  评论(0编辑  收藏  举报