• 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('
    ')
    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
    ",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
    ",mid);
                if(pd(mid)) L = mid;
                else R = mid - 1;
            }
            printf("%d
    ",L);
        }
        return 0;
    }
    
  • 相关阅读:
    jQuery瀑布流绝对定位布局(二)(延迟AJAX加载图片)
    jQuery图片上传裁剪插件imgAreaSelect(分析四) 上传服务器端
    jQuery表格的排序,
    jQuery图片上传裁剪插件imgAreaSelect(分析二) 同步显示图像位置信息
    jQuery图片上传裁剪插件imgAreaSelect(分析三) 如何获得选择域的图像信息
    JS Get URL param
    Dictionary 比List占用更多的内存
    修改金蝶采购订单的默认采购方式
    sqlserver 数据库操作记录 实现
    用Python模拟键盘输入
  • 原文地址:https://www.cnblogs.com/captain1/p/10269503.html
Copyright © 2020-2023  润新知