题目链接
题目大意: 从一个序列里删除若干个序列,使其成为回文序列。求其中最长的回文序列。
如果想最大限度的利用每一个数,那么左边区间的最后一个数字和右边区间的第一个数字肯定是相同的,同时,两边区间同一个数字出现的次数也是相同的。所以做法就是记录下每个数字出现的位置,然后分别从两头开始枚举,再从中间的区间找一个出现最多的数就行了。至于怎么快速的找出区间某个数字出现的次数,可以用前缀和来预处理然后遍历每个数字。因为每次枚举某个区间都要对所有数字进行遍历,显然(n)个数有(n)个位置,所以时间复杂度为(O(200n))。
const int maxn = 2e5+10;
vector<int> pos[205];
int arr[maxn], pre[maxn][205];
int main(void) {
int t;
scanf("%d", &t);
while(t--) {
int n;
scanf("%d", &n);
for (int i = 1; i<=n; ++i) {
scanf("%d", &arr[i]);
++pre[i][arr[i]];
for (int j = 1; j<=205; ++j) pre[i][j]+=pre[i-1][j];
pos[arr[i]].push_back(i);
}
int ans = 1;
for (int i = 1; i<=200; ++i) {
if (pos[i].empty()) continue;
int sz = pos[i].size(), l = 0, r = sz-1, cnt = 1;
//这里有一个pos[i][l]==pos[i][r]的情况(也就是序列中只有这一个数,不过ans初值为1可以忽略这种情况
while(pos[i][l]<pos[i][r]) {
for (int j = 1; j<=200; ++j) ans = max(ans, cnt*2+pre[pos[i][r]-1][j]-pre[pos[i][l]][j]);
++l, --r;
++cnt;
}
}
for (int i = 1; i<=200; ++i) {
pos[i].clear();
for (int j = 1; j<=200; ++j) pre[i][j] = 0;
}
printf("%d
", ans);
}
return 0;
}