给定一个长度为 n n n的序列 a a a。
我们定义三回文序列是形如 a . . . a ⏟ k 1 b . . . b ⏟ k 2 a . . . a ⏟ k 1 \underbrace{a...a}_{k_1}\underbrace{b...b}_{k_2}\underbrace{a...a}_{k_1} k1 a...ak2 b...bk1 a...a的序列,例如: [ 1 , 1 , 1 , 2 , 2 , 1 , 1 , 1 ] [1,1,1,2,2,1,1,1] [1,1,1,2,2,1,1,1]是一个三回文序列,而 [ 1 , 1 , 1 , 2 , 2 , 2 , 3 , 3 , 3 ] , [ 1 , 1 , 2 , 2 , 1 ] [1,1,1,2,2,2,3,3,3],[1,1,2,2,1] [1,1,1,2,2,2,3,3,3],[1,1,2,2,1]都不是三回文序列。
现在,希望你找到序列 a a a中最长的三回文序列的长度。
注意, k 1 , k 2 k_1,k_2 k1,k2可以为 0 0 0
第一行给出一个数字 T T T,表示 T T T组测试用例
对于每一个测试用例
第一行给出序列的长度 n n n
第二行给出序列 a 1 , a 2 , a 3 . . . a n a_1,a_2,a_3...a_n a1,a2,a3...an
对于每一个测试用例,输出最长的三回文序列的长度。
1 ≤ t ≤ 2000 1≤t≤2000 1≤t≤2000
1 ≤ ∑ n ≤ 2 ∗ 1 0 5 1≤\sum{n}≤2*10^5 1≤∑n≤2∗105, 1 ≤ a i ≤ 26 1≤a_i≤26 1≤ai≤26
6
8
1 1 2 2 3 2 1 1
3
1 3 3
4
1 10 10 1
1
26
2
2 1
3
1 1 1
7
2
4
1
1
3
通过样例,我们注意到三回文序列不一定是连续序列
也就是说1 1 2 2 3 2 1 1
的最长三回文序列是1 1 2 2 2 1 1
要想找出最长的三回文序列,我们就需要找出所有三回文序列
首先想到动态规划,但是很难找出最优子结构
然后因为是序列问题,我们想到了前缀和
基本思路:尝试 1 1 1$26$的每一种作为$a$,然后尝试所有可能的$k_1$,然后再尝试$1$ 26 26 26的每一种作为 b b b,每一种 b b b会对应一个 k 2 k_2 k2
这么直接看可能不太容易懂,可以暂时理解一下大概
由这个思路很容易看出我们要采用三重循环来实现
for (int a = 1; a <= 26; a++) {
for (int k1 = 0; k1 <= a的总数; k1++) {
for (int b = 1; b <= 26; b++) {
...
}
}
}
对应于每一个 a a a和 k 1 k_1 k1的组合( i i i和 j j j),我们想要知道左右两个索引,以便定下来 b b b序列所在的区间
for (int a = 1; a <= 26; a++) {
for (int k1 = 0; k1 <= a的总数; k1++) {
int l = 左索引, r = 右索引;
if (l > r) break;
for (int b = 1; b <= 26; b++) {
int k2 = [l+1, r-1]范围内b的数量;
max_len = max(max_len, k1 * 2 + k2);
}
}
}
好了,我们的伪代码写完了,接下来是如何把对应部分替换为真正的代码
可以看出,我们有两个要求:
(1)指定 a a a的数量 k 1 k_1 k1,我们要求返回它的索引 l , r l,r l,r
(2)指定某个区间范围,我们要求返回该范围内指定元素 b b b的数量
实现方式是维护两个数组sum[26][n], indices[26][n]
sum[i][j]
存储的前j
个元素的i
的数量
indices[i][j]
存储的是i
的前缀和为j
时在序列中的下标
如何使用这两个数组呢?
for (int a = 1; a <= 26; a++) {
for (int k1 = 0; k1 <= sum[a][n]; k1++) {
int l, r;
if (k1) {
l = indices[a][k1];
r = indices[a][sum[a][n] - k1 + 1];
}
else {
l = 0;
r = n + 1;
}
if (l > r) break;
else if (l == r) max_len = max(max_len, 2 * k1 - 1);
else {
for (int b = 1; b <= 26; b++) {
int k2 = sum[b][r - 1] - sum[b][l - 1];
max_len = max(max_len, k1 * 2 + k2);
}
}
}
}
最后,AC代码如下
#include
using namespace std;
const int max_num = 26;
const int max_len = 2e5;
int sum[max_num + 1][max_len + 1], indices[max_num + 1][max_len + 1];
int main() {
int t, n, x;
cin >> t;
for (int i = 0; i < t; i++) {
cin >> n;
for (int j = 1; j <= n; j++) {
cin >> x;
for (int k = 1; k <= max_num; k++) {
sum[k][j] = sum[k][j - 1];
}
sum[x][j]++;
indices[x][sum[x][j]] = j;
}
int ans = 0;
for (int a = 1; a <= 26; a++) {
for (int k1 = 0; k1 <= sum[a][n]; k1++) {
int l, r;
if (k1) {
l = indices[a][k1];
r = indices[a][sum[a][n] - k1 + 1];
}
else {
l = 0;
r = n + 1;
}
if (l > r) break;
else if (l == r) ans = max(ans, 2 * k1 - 1);
else {
for (int b = 1; b <= 26; b++) {
int k2 = sum[b][r - 1] - sum[b][l];
ans = max(ans, k1 * 2 + k2);
}
}
}
}
cout << ans << endl;
}
return 0;
}