CTSC2012 熟悉的文章

传送门

首先很容易想到对于所有的模式串建出广义后缀自动机,之后对于我们每一个要检查的文本串,先在SAM上跑,计算出来每一个位置能匹配到的最远的位置是多少。(就是当前点减去匹配长度)

之后……考虑DP……一开始我的状态设错了,设成了当前位置的最大的L的值,这样我就不知道怎么转移了……

于是换一个思路。考虑到其实我们可以判定L是否成立,于是改为二分答案,那么这次我们就用\(dp[i]\)表示到当前串第i位,已经被匹配为“熟悉”的总字符串长度。这样到最后只要判断一下是否大于90%即可。那么我们就有dp方程:

\[dp[i] = max_{j = i-maxl}^{i-L}(dp[j] + i - j,dp[i-1]) \]

这个方程还是很好理解的w…… 不过这样朴素的做法是\(O(n^2logn)\)的,但是这个方程因为左右端点都是递增的,那么我们就可以用单调队列维护一下。

然后就是喜闻乐见的单调队列优化DP。复杂度\(O(nlogn)\)

#include<bits/stdc++.h>
#define rep(i,a,n) for(register int i = a;i <= n;i++)
#define per(i,n,a) for(register int i = n;i >= a;i--)
#define enter putchar('\n')
using namespace std;
typedef long long ll;
const int M = 2000005;

int n,m,maxl[M],dp[M],head,tail,q[M],d;
string s;

struct Suffix
{
    int last,cnt,ch[M<<1][2],fa[M<<1],l[M<<1];
    void extend(int c)
    {
        int p = last,np = ++cnt;
        l[np] = l[p] + 1,last = cnt;
        while(p && !ch[p][c]) ch[p][c] = np,p = fa[p];
        if(!p) {fa[np] = 1;return;}
        int q = ch[p][c];
        if(l[q] == l[p] + 1) fa[np] = q;
        else
        {
            int nq = ++cnt;
            l[nq] = l[p] + 1,ch[nq][0] = ch[q][0],ch[nq][1] = ch[q][1];
            fa[nq] = fa[q],fa[np] = fa[q] = nq;
            while(ch[p][c] == q) ch[p][c] = nq,p = fa[p];
        }
    }
    void match(string s)
    {
        int k = s.length()-1,u = 1,len = 0;
        rep(i,0,k)
        {
            int c = s[i] - '0';
            while(u != 1 && !ch[u][c]) u = fa[u],len = l[u];
            if(ch[u][c]) u = ch[u][c],len++;
            maxl[i+1] = len;
        }
    }
}SAM;

bool pd(int x)
{
    rep(i,1,x-1) dp[i] = 0;
    head = 1,tail = 0;
    rep(i,x,d)
    {
        dp[i] = dp[i-1];
        while(head <= tail && dp[q[tail]] - q[tail] <= dp[i-x] - i + x) tail--;
        q[++tail] = i - x;
        while(head <= tail && q[head] < i - maxl[i]) head++;
        if(head <= tail) dp[i] = max(dp[q[head]] - q[head] + i,dp[i]);
    }
    //printf("%d %d\n",x,dp[d]);
    return dp[d] * 10 >= d * 9;
}

int main()
{
    ios::sync_with_stdio(0);
    cin >> n >> m;
    SAM.cnt = 1;
    rep(i,1,m)
    {
        SAM.last = 1;
        cin >> s,d = s.length()-1;
        rep(i,0,d) SAM.extend(s[i] - '0');
    }
    rep(i,1,n)
    {
        cin >> s,d = s.length();
        SAM.match(s);
        //rep(i,1,d) printf("%d ",maxl[i]);enter;
        int L = 0,R = d;
        while(L < R)
        {
            int mid = (L + R + 1) >> 1;
            //printf("%d\n",mid);
            if(pd(mid)) L = mid;
            else R = mid - 1;
        }
        printf("%d\n",L);
    }
    return 0;
}
posted @ 2019-01-14 22:46  CaptainLi  阅读(179)  评论(0编辑  收藏  举报