#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;
}

End

posted @ 2021-10-14 16:14  xixike  阅读(34)  评论(0编辑  收藏  举报