Gym 100543G Virus synthesis 題解

Laijinyi發表於2024-10-07

Solution

首先只考慮迴文串的答案;我們重點考慮的是偶迴文串

結論:對於偶迴文串 \(u\),從其最長的長度小於等於他的一半的迴文字尾,或其父親轉移過來,一定是最優的

證明:

\(u\) 的一個迴文子串為 \(v\)(不是父親),你要讓 \(v\to u\) 的轉移最優

首先 \(v\) 不能跨過 \(u\) 的中點,因為此時“從父親轉移過來”一定不會更劣

其次 \(v\) 不能位於 \(u\) 的中間(非前字尾),因為這種情況 \(u\) 的祖先已經考慮到了

證完了

於是轉移即可;對於奇迴文串,他只能從其父親或 fail 轉移而來,證明是類似的。

做完了。

Code

#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
#define reo(i, j, k) for (int i = (j); i >= (k); --i)
typedef long long ll;
const int N = 1e5 + 10;
string s;
int n;

int cur, last, sz, len[N], fail[N], nxt[N][4], f[N], half[N];
void init() {
	while (sz >= 0) {
		len[sz] = fail[sz] = f[sz] = half[sz] = 0;
		rep(i, 0, 3) nxt[sz][i] = 0;
		--sz;
	}
	cur = last = 0, sz = 1, len[1] = -1, fail[0] = 1;
}
int getfail(int u, int i) {
	while (i - len[u] - 1 < 0 || s[i - len[u] - 1] != s[i]) u = fail[u];
	return u;
}
void Solve() {
	cin >> s, n = s.length(), init();
	int ans = 114514;
	auto Get = [&](char c) {
		if (c == 'A') return 0;
		if (c == 'G') return 1;
		if (c == 'T') return 2;
		if (c == 'C') return 3;
		return 0;
	};
	rep(i, 0, n - 1) {
		int p = getfail(last, i), ch = Get(s[i]);
		if (!nxt[p][ch]) {
			cur = ++sz;
			fail[cur] = nxt[getfail(fail[p], i)][ch];
			nxt[p][ch] = cur;
			len[cur] = len[p] + 2;
			if (len[cur] & 1) {
				f[cur] = min(f[p] + 2, f[fail[cur]] + len[cur] - len[fail[cur]]);
			} else {
				int q = getfail(half[p], i);
				while (len[q] + 2 > len[cur] / 2) q = getfail(fail[q], i);
				if (len[cur] > 2 && (q || s[i] == s[i - 1])) q = nxt[q][ch];
				else q = 0;
				half[cur] = q;
				if (p) {
					f[cur] = min(f[p] + 1, f[half[cur]] + len[cur] / 2 - len[half[cur]] + 1);
				} else {
					f[cur] = 2;
				}
			}
			ans = min(ans, n - len[cur] + f[cur]);
		}
		last = nxt[p][ch];
	}
	cout << ans << '\n';
}

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	int t;
	cin >> t;
	while (t--) Solve();
	return 0;
}

相關文章