P4022 [CTSC2012]熟悉的文章

题目

P4022 [CTSC2012]熟悉的文章

题目大意:多个文本串,多个匹配串,我们求\(L\)\(L\)指(匹配串中\(≥L\)长度的子串出现在文本串才为"熟悉",使得匹配串整个近似"熟悉")的最大值

近似"熟悉":将匹配串分割,所有串总"熟悉"长度有\(90\%\)以上

做法

首先明确一点,\(L_1<L_2\),则\(L_1\)的熟悉程度\(≥L_2\)的熟悉程度

比如文本串\('adc'\),匹配串\('ac'\)\(L=2\)熟悉程度为\(0\%\)\(L=1\)熟悉程度为\(100\%\)

故我们可以二分\(L\)然后去\(dp\),设数组\(dp_i\)为以\(i\)结尾前的熟悉长度

则:\(dp_i=max\{dp_j+(i-j)\}(j\in [i-match_i,i-L])\),其中\(match_i\)是以\(i\)结尾的后缀所能匹配的最长长度

\(i-match_i\)\(i-L\)都是单调递增的,单调队列优化一下

\(O(nlogn)\)

My complete code

几个月前惨不忍睹的码风虽然现在依旧是,将就看吧

#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
typedef long long LL;
const LL maxn=400000;
LL n,m,nod=1,last,Len;
LL len[maxn],son[maxn][26],fail[maxn],val[maxn],dp[maxn];
char s[maxn];
inline void Insert(LL c){
    LL p=last,np=++nod;
    last=np;
    len[np]=len[p]+1;
    while(p&&!son[p][c]){
        son[p][c]=np;
        p=fail[p];
    }
    if(!p)
        fail[np]=1;
    else{
        LL q=son[p][c];
        if(len[q]==len[p]+1)
            fail[np]=q;
        else{
            LL nq=++nod;
            len[nq]=len[p]+1;
            memcpy(son[nq],son[q],sizeof(son[q]));
            fail[nq]=fail[q];
            fail[q]=fail[np]=nq;
            while(p&&son[p][c]==q){
                son[p][c]=nq;
                p=fail[p];
            }
        }
    }
}
inline void Match(){
    LL now=1,l=0;
    for(LL i=1;i<=Len;++i){
        LL c=s[i]-'0';
        while(now&&!son[now][c]){
            now=fail[now];
            l=len[now];
        }
        if(now){
            now=son[now][c];
            ++l;
        }else{
            now=1;
            l=0;
        }
        val[i]=l;
    }
}
inline bool check(LL L){
    LL head=1,tail=0;
    LL que[maxn];
    for(LL i=1;i<=L-1;++i)
        dp[i]=0;
    for(LL i=L;i<=Len;++i){
        while(head<=tail&&dp[que[tail]]-que[tail]<dp[i-L]-(i-L))
            --tail;
        que[++tail]=i-L;
        while(head<=tail&&que[head]<i-val[i])
            ++head;
        dp[i]=dp[i-1];
        if(head<=tail)
            dp[i]=max(dp[i],dp[que[head]]-que[head]+i);
    }
    return dp[Len]*10>=Len*9;
}
inline LL Solve(){
    LL l=1,r=Len,ans=0;
    Match();
    while(l<=r){
        LL mid=(l+r)>>1;
        if(check(mid)){
            ans=mid;
            l=mid+1;
        }else
            r=mid-1;
    }
    return ans;
}
int main(){
    scanf("%lld%lld",&n,&m);
    while(m--){
        scanf(" %s",s+1);
        Len=strlen(s+1);
        last=1;
        for(LL i=1;i<=Len;++i)
            Insert(s[i]-'0');
    }
    while(n--){
        scanf(" %s",s+1);
        Len=strlen(s+1);
        printf("%lld\n",Solve());
    }
}

转载于:https://www.cnblogs.com/y2823774827y/p/10317388.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值