【manager】Prefix-Suffix Palindrome (Hard version)—CF1326D2

Prefix-Suffix Palindrome (Hard version)—CF1326D2

  • 这道1800分的题果然不好写。
  • 考查了马拉车算法和一点点的思维(但不幸的是我一开始读错题了,t 字符串并不需要是原字符串的子串,但我当成需要是原字符串来想了)
  • 代码的实现是本题的重中之重,因为其中有复杂的边界问题,以及一个玄学问题。

ACode:

#include <bits/stdc++.h>
#define int long long
#define sz(a) ((int)a.size())
#define all(a) a.begin(), a.end()
using namespace std;
using PII = pair<int, int>;
using i128 = __int128;
const int N = 2e5 + 10;

int n;
string str;

void manager(string s) {
	s = s + s + "  "; // 扩大s的大小,防止的后边使用s[i]时出现段错误
	// 马拉车算法经典的预处理,不再解释
	s.insert(s.begin(), '@');
	n *= 2;
	for (int i = n; i; i -= 2) {
		s[i] = s[i / 2];
		s[i - 1] = '#';
	}
	s[n + 1] = '#';
	s[n + 2] = '$';
	
	vector <int> a(n + 1, 0);
	int l = 1, r = 1;
	int idx = 1;
	for (int i = 1; i <= n; i ++) {
		if (i <= r) {
			a[i] = min(a[l + r - i], r - i + 1);
		}
		while (s[i - a[i]] == s[i + a[i]]) {
			a[i] ++;
		}
		if (i + a[i] - 1 > r) {
			l = i - a[i] + 1;
			r = i + a[i] - 1;
		}
		// 如果当前回文子串是前缀或后缀,并且长度大于idx位置的回文子串
		if ((a[i] == i || a[i] == n - i + 2) && a[i] > a[idx]) {
			idx = i;
		}
	}
	
	for (int i = max(idx - a[idx] + 1, 1ll); i <= min(idx + a[idx] - 1, n); i ++) {
		/*
		  因为n == 0的时候a[idx]会越界,所以for循环如果这样写:
		  for (int i = idx - a[idx] + 1; i <= idx + a[idx] - 1; i ++)
		  那么在for循环前要加上:
		  if (n == 0)
			  return;
		  或n == 0的时候就直接不进入manager函数。
		 */
		if (s[i] != '#') {
			cout << s[i];
		}
	}
}

void solve() {
	cin >> str;
	
	int sz_str = sz(str); // 记录原数组长度,后边要多次使用
	int len = 0; // 最大的满足“任意i ∈ [0, len - 1], s[i] = s[sz_str - i]”的len值
	while ((len + 1) * 2 <= sz_str && str[len] == str[sz_str - 1 - len]) { // 计算len
		len ++;
	}
	
	cout << str.substr(0, len); // 输出前缀
	n = sz_str - 2 * len; // 中间需要用马拉车的字符串长度
	manager(str.substr(len, n)); 
	// 使用马拉车算法找到s字符串的所有前缀、后缀字符串中最长的回文子串
	cout << str.substr(sz_str - len, len) << "\n"; // 输出后缀
}

signed main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T = 1;
	cin >> T; cin.get();
	while (T --) solve();
	return 0;
}

另外,我说的玄学问题就是manager中用string不会超时,而用char* 字符串就会超时,我也不知道是怎么回事。
以后还是少用char* 字符串吧。
附char* 超时代码:

#include <bits/stdc++.h>
#define int long long
#define sz(a) ((int)a.size())
#define all(a) a.begin(), a.end()
using namespace std;
using PII = pair<int, int>;
using i128 = __int128;
const int N = 1e5 + 10;

int n;
string str;
char s[2 * N];

void manager() {
	s[0] = '@';
	n *= 2;
	for (int i = n; i; i -= 2) {
		s[i] = s[i / 2];
		s[i - 1] = '#';
	}
	s[n + 1] = '#';
	s[n + 2] = '$';
	
	vector <int> a(n + 1, 0);
	int l = 1, r = 1;
	int idx = 1;
	for (int i = 1; i <= n; i ++) {
		if (i <= r) {
			a[i] = min(a[l + r - i], r - i + 1);
		}
		while (s[i - a[i]] == s[i + a[i]]) {
			a[i] ++;
		}
		if (i + a[i] - 1 > r) {
			l = i - a[i] + 1;
			r = i + a[i] - 1;
		}
		if ((a[i] == i || a[i] == n - i + 2) && a[i] > a[idx]) {
			idx = i;
		}
	}
	
	for (int i = max(idx - a[idx] + 1, 1ll); i <= min(idx + a[idx] - 1, n); i ++) {
		if (s[i] != '#') {
			cout << s[i];
		}
	}
}

void solve() {
	cin >> str;
	
	int sz_str = sz(str);
	int len = 0;
	while ((len + 1) * 2 <= sz_str && str[len] == str[sz_str - 1 - len]) {
		len ++;
	}
	
	cout << str.substr(0, len);
	n = sz_str - 2 * len;
	for (int i = len, j = 1; j <= n; i ++, j ++) {
		s[j] = str[i];
	}
	manager();
	cout << str.substr(sz_str - len, len) << "\n";
}

signed main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T = 1;
	cin >> T; cin.get();
	while (T --) solve();
	return 0;
}
posted @ 2023-09-17 20:39  AC_2656  阅读(15)  评论(0)    收藏  举报