#3790. 神奇项链 题解
Description
\(bzoj\) 上的题,黑暗爆炸OJ传送门
Solution
是一道 \(manacher\) 算法的应用题。
首先转换一下题意:给定一个字符串,让我们求最少的回文串个数去覆盖它。
所以先跑一遍 \(manacher\),求出每个回文串的左右边界,存到一个数组里。此时这道题就变成了让我们选出最少的区间覆盖 \(1\) ~ \(n\)。
一道非常经典的贪心,我们先按左端点排序,左端点相同的点按右端点排序。
枚举区间时,记录一下最右边覆盖到哪里了,在所有当前合法的区间中选出右端点最靠右的区间,同时更新最右覆盖及合并次数即可(感觉说不清楚,具体看代码吧)。
注意到 \(aba\) 和 \(aca\) 可以合并为 \(abaaca\),所以判断时,要判断 \(q[i].l - 1 <= maxr\)。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
char str[N], s[N << 1];
int n, p[N];
struct node{
int l, r;
bool operator < (const node &b) const{
return l != b.l ? l < b.l : r < b.r;
}
}a[N];
inline void init(){
n = strlen(str + 1);
s[0] = '*', s[1] = '#';
for(int i = 1; i <= n; i++)
s[i << 1] = str[i], s[(i << 1) + 1] = '#';
n = (n << 1) + 1;
}
inline void manacher(){
int id = 0, mx = 0;
for(int i = 1; i <= n; i++){
if(i < mx) p[i] = min(p[(id << 1) - i], mx - i);
else p[i] = 1;
while(i - p[i] >= 1 && i + p[i] <= n && s[i - p[i]] == s[i + p[i]]) p[i]++;
if(i + p[i] > mx) id = i, mx = i + p[i];
}
}
inline int solve(){
memset(p, 0, sizeof(p));
init();
manacher();
for(int i = 1; i <= n; ++i)
a[i].l = i - p[i] + 1, a[i].r = i + p[i] - 1;
sort(a + 1, a + 1 + n);
int r = 0, mx = 0, cnt = 0;
for(int i = 1; i <= n;){
if(a[i].l - 1 <= r) mx = max(mx, a[i].r), ++i;
else r = mx, mx = 0, cnt++;
}
cnt += (r < n);
return cnt - 1;
}
int main(){
while(scanf("%s", str + 1) != EOF)
printf("%d\n", solve());
return 0;
}