【2021 CSP 7连测 day3】回文串

回文串

题意

有一个字符串 \(w\),你可以不改变它或者只选择一个位置并更改成任意的字母,使得这个字符串中所有回文子串的数量最大

思路

为了方便描述,我们假设我们只处理奇数长度的回文,很显然,大不了加入一些非法的字符,
就可以把偶数长度的字符串处理变成奇数长度的。

因为长度是奇数,所以回文的中心是其中间的字符。首先,对于每个位置 \(a(1 \leq a \leq n)\) ,确定中心在 \(a\) 的最长回文的半径 \(r_a\)(最长的回文中心在 \(a\)\(w_{a−r_a , a+r_a}\))。显然,字符串的价值等于所有位置 \(r_a + 1\) 的总和。

有很多种方法来计算所有数字 \(r_a\)

  • 对于每个位置 a,通过二分搜索确定值 \(r_a\),需要用 \(\operatorname{hash}\) 配合。

  • 直接用 \(\operatorname{Manacher}\) 算法


现在我们需要考虑如果位置 \(i\) 的字符转换为 \(c\),价值如何变化。显然转换后,一些回文消失,一些新的回文出现。

  1. 首先,我们计算有多少现有回文消失。我们只考虑中心在 \(a\) 的回文。显然只有当转换字符的位置来自区间 \([a − r_a, a + r_a]\) 时才会消失。所以,我们可以用扫描线算法来计算这部分。

  1. 然后考虑新的回文数。假设在位置 \(i\) 转换为字符 \(c\) 后,出现了一个新的回文,其中心在 \(a\),其中 \(i < a\)。令 \(d = a − i\)。因此,字符串 \(w_a−d, a + d\) 在转换之前不是回文,但现在是。

    由于 \(w_a−d,a+d\)现在是回文,所以 \(w_a−d + 1, a + d−1\) 也是,但是这个串没有改变,所以它是转换前的回文。因此,\(w_a−d + 1, a + d−1\) 是转换前中心在 \(a\) 中的最长回文,因此 \(d\) 等于 \(r_a + 1\)。位置 \(a − r_a − 1\) 处的字符转换为位置 \(a + r_a + 1\) 处的字符,反之亦然。

    因此,类似于我们如何计算 \(r_a\)(用 \(\operatorname{hash}\) 进行二分查找来比较字符串),我们可以计算在两种情况下出现的新回文数。

Code

点击查看代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using std::max;
using std::min;
typedef long long ll;
const int N = 100005;
const ll base = 131;
const ll mod = 1000000007; 
char s1[N], s2[N];
ll h1[N], h2[N], pow[N];
ll f[N][26]; // 产生的回文串 
ll fk[N], fl[N]; // 消失的回文串 
ll n;
void calc_hash(char * s, ll * h) {
	h[0] = s[0];
	for (ll i = 1; i < n; i++) 
		h[i] = (h[i - 1] * base + s[i]) % mod;
}
ll get_hash(ll l, ll r, ll * h) {
	return (h[r] - h[l - 1] * pow[r - l + 1] % mod + mod) % mod;
}
bool check(ll a, ll b, ll c, ll d) {
	return get_hash(a, b, h1) == get_hash(n - d - 1, n - c - 1, h2);
}
ll Base = 0;
void manacher() { // 奇回文 
	for (ll i = 0; i < n; i++) {
		for (ll j = i; j <= i + 1; j++) {
			ll low = 0, high = min(i + 1, n - j);
			while (low < high) {
				ll mid = (low + high + 1) / 2;
				if (check(i - mid + 1, i, j, j + mid - 1)) 
					low = mid; 
                else high = mid - 1;
			}
			ll r = low;
			Base += r;
			if (i == j) { // 奇回文 
				fk[j + 1] += - 1, fk[j + r] -= - 1;
				fl[j + 1] += j + r, fl[j + r] -= j + r;
				fk[i - r + 1] += 1, fk[i] -= 1;
				fl[i - r + 1] += - (i - r), fl[i] -= - (i - r);
			} 
            else { // 偶回文 
				fk[j] += - 1, fk[j + r] -= - 1;
				fl[j] += j + r, fl[j + r] -= j + r;
				fk[i - r + 1] += 1, fk[i + 1] -= 1;
				fl[i - r + 1] += - (i - r), fl[i + 1] -= - (i - r);
			}
			int x = i - r, y = j + r;
			if (x < 0 || y >= n) continue;
			low = 0;
			high = min(i + 1, n - j) - (r + 1);
			while (low < high) {
				int mid = (low + high + 1) / 2;
				if (check(x - mid, x - 1, y + 1, y + mid)) 
					low = mid;
				else high = mid - 1;
			}
			f[x][s1[y] - 'a'] += low + 1;
			f[y][s1[x] - 'a'] += low + 1;
		}
	}
}
int main() 
{
	scanf("%s", s1);
	n = strlen(s1);
	for (int i = 0; i < n; i++) s2[i] = s1[n - i - 1];
	pow[0] = 1;
	for (int i = 1; i <= n; i++) 
		pow[i] = pow[i - 1] * base % mod;
	calc_hash(s1, h1); calc_hash(s2, h2);
	manacher();
	ll change = 0, k = 0, l = 0;
	for (ll i = 0; i < n; i++) {
		k += fk[i], l += fl[i];
		for (ll j = 0; j < 26; j++) 
			if (j + 'a' != s1[i]) 
				change = max(change, f[i][j] - (i * k + l));
	}
	printf("%lld", Base + change);
	return 0;
}
posted @ 2021-12-18 21:26  Tritons  阅读(86)  评论(0)    收藏  举报
页脚Html代码: