题解 P4022【[CTSC2012]熟悉的文章】

$\text{Link}$

upd 2021.7.4:更改部分用词.

题意

给你 $m$ 个文本串 $T_{1,2,...,m}$,$n$ 次询问,对于一个字符串 $A$ 满足 $A=A_1A_2A_3...A_k$,其中 $\forall i\in[1,k],|A_i|\ge L$。求 $L_0=\max \{L|(\sum_{i=1}^k|A_i|[\exists j\in[1,m],A_i\text{ in substrings}(T_j)])\ge|A|\times 0.9\}$。

$\sum|A|+\sum|T_i|\le 1.1\times 10^6$。

思路

首先看到 $\max$ 先知道需要二分答案 $L_0$,然后考虑 $\text{check}$ 一个 $L$ 是否能够满足条件。

看到文本分段自然地想到 $\text{dp}$。

我们设 $dp_i$ 表示以 $i$ 作为当前段结尾,最长可以匹配多长的字符串,则有转移 $dp_i=\max(dp_j+i-j,dp_{i-1})(j\in[i-match_i,i-L])$,其中 $match_i$ 为以 $i$ 作为字符串结尾的字符串在文本串中能够匹配的最长长度。

然后处理 $match_i$ 可以开一个广义 $\text{SAM}$ 统计,与 $\text{LCS}$ 一样,这里不多讲解。

现在我们得到了一个 $O(\sum|T|+\sum|A|^2\log |A|)$ 的算法,显然不能通过。

然后我们考虑 $i$ 显然为严格递增的,于是我们可以维护 $dp_j-j$ 的单调队列然后就可以 $O(1)$ 转移了,时间复杂度降至 $O(\sum|T|+\sum|A|\log |A|)$。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{

}
const int N=2.2e6+10;
int n,m,q;
int pos[N],ansi[N];
char str[N];
struct SAM{
    struct statu{
        int len,link;
        int ch[2];
    }a[N];
    int siz,last;
    SAM(){
        a[0].len=0;
        a[0].link=-1;
        last=0;
    }
    inline void insert(int c){
        int cur=++siz;
        a[cur].len=a[last].len+1;
        int p=last;
        while(p!=-1&&!a[p].ch[c]){
            a[p].ch[c]=cur;
            p=a[p].link;
        }
        if(~p){
            int q=a[p].ch[c];
            if(a[p].len+1==a[q].len){
                a[cur].link=q;
            }else{
                int clone=++siz;
                a[clone]=a[q];
                a[clone].len=a[p].len+1;
                while(p!=-1&&a[p].ch[c]==q){
                    a[p].ch[c]=clone;
                    p=a[p].link;
                }
                a[q].link=a[cur].link=clone;
            }
        }else{
            a[cur].link=0;
        }
        last=cur;
    }
    inline void insert(){
        static char str[N];
        for(int k=1;k<=m;k++){
            readstr(str);
            int len=strlen(str+1);
            last=0;
            for(int i=1;i<=len;i++){
                insert(str[i]-'0');
            }
        }
    }
    inline void calc(){
        int anss=0;
        int p=1;
        for(int i=1;i<=n;i++){
            int c=str[i]-'0';
            if(a[p].ch[c]){
                anss++;
                p=a[p].ch[c];
            }else{
                while(p!=-1&&!a[p].ch[c]){
                    p=a[p].link;
                }
                if(~p){
                    anss=a[p].len+1;
                    p=a[p].ch[c];
                }else{
                    anss=0;
                    p=1;
                }
            }
            ansi[i]=anss;
        }
    }
    inline bool check(int L){
        static int q[N],dp[N];
        int head=1,tail=0;
        for(int i=1;i<L;i++) dp[i]=0;
        for(int i=L;i<=n;i++){
            dp[i]=dp[i-1];
            while(head<=tail&&dp[q[tail]]-q[tail]<=dp[i-L]-i+L) tail--;
            q[++tail]=i-L;
            while(head<=tail&&q[head]<i-ansi[i]) head++;
            if(head<=tail) dp[i]=max(dp[i],dp[q[head]]+i-q[head]);
        }
        return n*0.9<=dp[n];
    }
}sam;
int main(){
    q=read(),m=read();
    sam.insert();
    while(q--){
        n=readstr(str);
        sam.calc();
        int l=0,r=n;
        while(l<=r){
            int mid=l+r>>1;
            if(sam.check(mid)) l=mid+1;
            else r=mid-1;
        }
        write(l-1);
        putc('\n');
    }
    flush();
    return 0;
}

再见 qwq~

posted @ 2021-06-21 18:34  ffffyc  阅读(11)  评论(0)    收藏  举报  来源