【后缀自动机】[BZOJ 2806]Cheat

题目描述:就是给你多个01串当作字典然后给你另一个01串问你这个01串长度90%以上被匹配时的分段匹配的段的最大值L0(这个最大值是当前分段每一段的最小值,就是每一段长度都大于这个值,求这个值的最大值)
我看到这道题就知道要把所有的字典的串合成一个串来搞,原来弄过一个AC自动机的题目和这个描述很像,自然而然就这么弄了,建立SAM的时候每一个串之间弄个分隔符。原因不说了。现在我们只要先求出每一个位置向前能够匹配的最大值f(i)为什么只求最大值呢?因为每一个位置最大值能够向前匹配,那么小于最大值的串显然是最大值的后缀子串,显然仍然能够匹配,所以只需要求每个位置的能够匹配的最大值就可以了。然后怎么搞呢枚举每一段显然不可能,那这种情况下二分吧,二分当前的L0 然后怎么检测当前的L0是否可用呢,另dp(i)为1-i位能够被L0匹配的最大数量
首先因为dp(i)>=dp(i1)所以我们不用看if(i)之前的位置,因为dp(if(i))>=dp(if(i)k)然后发现长度起码为L0所以我们只需要关注[if(i),iL0]这个区间内的dp值就行了
所以让dp(i)最大那么dp(i)=max(dp(j)+(ij))if(i)1<j<iL0+1那么这个算法的复杂度是O(n2logn)显然满足不了,那么进行优化dp(i)=max(dp(j)j)+i那么dp(i)i=max(dp(j)j)那么我们另g(i)=dp(i)i求每一个点之前的区间内的最大值,试试看单调队列优化,如果要用单调队列优化需要证明if(i)是单调递增的,需要证明f(i)是单调递减的那么我们发现如果有一个发现如果前一个串a2a3...an显然是a2a3...anan+1如果a1a2a3...anan+1可以被匹配那么显然a1a2a3...an也可以被匹配那么前一个就不是a2a3...an那么f(i+1)<=f(i)+1那么这个时候区间的左端点要么向右移动要么不动,然后现在用单调队列优化就行了,每次显然是多了一个g(iL0) 。。。wa了5个小时,发现是忘了清零。。。好了现在复杂度变成O(nlogn)

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int MAXN = 2200010;
const int INF = 100000000;
struct node{
    node *p[4], *pre;
    int len;
}Edges[MAXN*2+10], *ecnt=Edges+1,*root=Edges,*last=Edges, *st[MAXN*2+10];
void Insert(int w){
    node *np = ecnt++;
    node *p = last;
    np->len = p->len+1;
    while(p&&!p->p[w])
        p->p[w]=np, p=p->pre;
    if(!p){
        np->pre = root;
    }else{
        node *q = p->p[w];
        if(p->len+1 == q->len){
            np->pre = q;
        }else{
            node *nnd = ecnt++;
            memcpy(nnd->p, q->p, sizeof (nnd->p));
            nnd->len = p->len+1; nnd->pre = q->pre; q->pre = nnd; np->pre = nnd;
            while(p&&p->p[w]==q)
                p->p[w]=nnd, p=p->pre;
        }
    }
    last = np;
}
const int MAXT = 2200010;
char s[MAXT+10];
int dp[MAXT+10], dp2[MAXT+10], que[MAXT];
bool check(int l){
    //dp2[i] = max{dp2[j]+i-j}   i-out  dp2[i]-i = max{dp2[j]-j}//
    int len = strlen(s);
    int lk=0, rk=0, ins;
    for(int i=0;i<l;i++)
        dp2[i] = 0;
    for(int i=l;i<=len;i++){
        dp2[i] = dp2[i-1];
        ins = i-l;
        while(lk<rk&&dp2[ins]-ins>=dp2[que[rk-1]]-que[rk-1]) rk--;
        que[rk++] = ins;
        while(lk<rk&&que[lk]<i-dp[i]) lk++;
        if(lk < rk)
            dp2[i] = max(dp2[i], dp2[que[lk]] - que[lk] + i);
    }
    return dp2[len] * 10 >= len * 9;
}
int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    for(int i=0;i<m;i++){
        scanf("%s", s);
        int len = strlen(s);
        for(int j=0;j<len;j++)
            Insert(s[j]-'0');
        Insert(2);
    }
    for(int i=0;i<n;i++){
        node *now = root;
        scanf("%s", s);
        int L = 0, len = strlen(s);
        for(int j=0;j<len;j++){
            int idx = s[j]-'0';
            if(now->p[idx]){
                now = now->p[idx];
                L++;
            }else{
                while(now && !now->p[idx])
                    now = now->pre;
                if(!now){
                    now = root;
                    L = 0;
                }else{
                    L = now->len + 1;
                    now = now->p[idx];
                }
            }
            dp[j+1] = L;
        }
        int l=1, r=len, ans=0;
        while(l <= r){
            int mid = (l + r) >> 1;
            if(check(mid)){ l = mid+1; ans = mid;
            }else r = mid-1;
        }
        printf("%d\n", ans);
    }

    return 0;
}

posted on 2015-06-11 22:28  JeremyGuo  阅读(202)  评论(0编辑  收藏  举报

导航