ZigZagK的博客
[广义后缀自动机+二分]2021-2022 ACM-ICPC Brazil Subregional Programming Contest B【Beautiful Words】题解
2022年3月2日 16:13
ICPC
查看标签

题目概述

Beautiful Words

解题报告

先把 $A$ 复制一份,令 $B_i=A[i-n+1,i]$ 。

然后二分答案 $mid$ ,这样就只需要验证是否存在 $i\in[n,2n-1]$ 使得 $B_i$ 中不存在长度 $>mid$ 的 $S$ 中字符串的子串。

接下来考虑二分验证,如果 $A[i-len+1,i]$ 是 $S_k$ 的一个子串,说明 $j\in[i,i-len+n]$ 的 $B_j$ 都存在长度为 $len$ 的 $S$ 中字符串的子串。可以发现 $len$ 越小,区间 $[i,i-len+n]$ 越长,因此我们只需要考虑 $len=mid+1$ 的情况,然后考虑所有 $B$ 的违法区间 $[i,i-(mid+1)+n]$ 是否覆盖了 $[n,2n-1]$ ,如果没有覆盖说明存在一个合法位置。

因此,如果我们能求出 $suf_i$ 表示 $A$ 以 $i$ 结尾往前能匹配的最长长度,就可以知道 $i$ 位置是否存在 $>mid$ 的子串。

不难想到对 $S$ 中的字符串建广义后缀自动机,然后将 $A$ 放到自动机上匹配就可以求出 $suf_i$ 。

示例程序

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200000;

int n,m;char s[maxn+5],t[maxn+5];
int pl=1,ro=1,son[maxn+5][26],fai[maxn+5],MAX[maxn+5];
int suf[maxn+5],c[maxn+5];

int Extend(int p,int c){
    if (son[p][c]){
        int q=son[p][c];if (MAX[p]+1==MAX[q]) return q;
        int nq=++pl;MAX[nq]=MAX[p]+1;for (int i=0;i<26;i++) son[nq][i]=son[q][i];
        fai[nq]=fai[q];fai[q]=nq;while (p && son[p][c]==q) son[p][c]=nq,p=fai[p];
        return nq;
    } else {
        int np=++pl;MAX[np]=MAX[p]+1;
        while (p && !son[p][c]) son[p][c]=np,p=fai[p];
        if (!p) {fai[np]=ro;return np;}
        int q=son[p][c];if (MAX[p]+1==MAX[q]) {fai[np]=q;return np;}
        int nq=++pl;MAX[nq]=MAX[p]+1;for (int i=0;i<26;i++) son[nq][i]=son[q][i];
        fai[nq]=fai[q];fai[q]=fai[np]=nq;while (p && son[p][c]==q) son[p][c]=nq,p=fai[p];
        return np;
    }
}
bool check(int mid){
    for (int i=0;i<(n<<1);i++) c[i]=0;
    for (int i=1;i<(n<<1);i++)
        if (suf[i]>mid){
            int L=i,R=min(i-(mid+1)+n,(n<<1)-1);
            c[L]++;c[R+1]--;
        }
    for (int i=1;i<(n<<1);i++) c[i]+=c[i-1];
    for (int i=n;i<(n<<1);i++) if (!c[i]) return true;
    return false;
}
int main(){
    scanf("%d%d%s",&n,&m,s+1);
    for (int i=1;i<n;i++) s[i+n]=s[i];
    for (int i=1;i<=m;i++){
        scanf("%s",t+1);
        for (int j=1,p=ro;t[j];j++) p=Extend(p,t[j]-'a');
    }
    for (int i=1,p=ro,len=0;i<(n<<1);i++){
        while (p && !son[p][s[i]-'a']) p=fai[p],len=MAX[p];
        if (p) p=son[p][s[i]-'a'],len++; else p=ro,len=0;
        suf[i]=min(i,min(len,n));
    }
    int L=0,R=n;
    for (int mid=L+(R-L>>1);L<=R;mid=L+(R-L>>1))
        check(mid)?R=mid-1:L=mid+1;
    printf("%d\n",L);
    return 0;
}
版权声明:本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处!