zoukankan      html  css  js  c++  java
  • bzoj2806

    广义后缀自动机+二分+单调队列+dp

    这道题其实就是一个简单dp,dp[i]表示匹配到i最长匹配多少,设val[i]表示当前位置和原串的最长公共长度,二分的长度是L,那么要求dp[i]=max(dp[i-1],dp[j]+i-j)要求L<=i-j<=val[i],那么也就是j>=i-val[i],前面的l每次把不符合的L>i-j弹掉,由于val[i]每次最多增加1,所以i-val[i]是不增的,所以可以用单调队列维护。最先开始我还想怎么在自动机上dp,结果直接求出val就行了。

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 2.2e6 + 5; 
    int n, m;
    int q[N], dp[N], val[N];
    char s[N];
    namespace SAM
    {
        struct node {
            int val, par;
            int ch[2];
        } t[N];
        int last = 1, sz = 1, root = 1;
        int nw(int _)
        {
            t[++sz].val = _;
            return sz;
        }
        void extend(int c)
        {
            int p = last, np = nw(t[p].val + 1);
            while(p && !t[p].ch[c]) t[p].ch[c] = np, p = t[p].par;
            if(!p) t[np].par = root;
            else
            {
                int q = t[p].ch[c];
                if(t[q].val == t[p].val + 1) t[np].par = q;
                else
                {
                    int nq = nw(t[p].val + 1);
                    memcpy(t[nq].ch, t[q].ch, sizeof(t[q].ch));
                    t[nq].par = t[q].par;
                    t[q].par = t[np].par = nq;
                    while(p && t[p].ch[c] == q) t[p].ch[c] = nq, p = t[p].par; 
                }   
            }
            last = np;
        }
    } using namespace SAM; 
    bool check(int L, int n)
    {
        int l = 1, r = 0, ans = 0;
        for(int i = 1; i <= n; ++i) 
        {
            dp[i] = dp[i - 1];
            if(i < L) continue;
            while(l <= r && dp[i - L] + L > dp[q[r]] + (i - q[r])) --r;
            q[++r] = i - L;
            while(l <= r && i - q[l] > val[i]) ++l;
            if(l > r || i - q[l] < L || i - q[l] > val[i]) continue;
            dp[i] = max(dp[i - 1], dp[q[l]] + (i - q[l]));
        }
        return dp[n] * 10 >= 9 * n;
    }
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; ++i)
        {
            last = root;
            scanf("%s", s + 1);
            int len = strlen(s + 1);
            for(int j = 1; j <= len; ++j) extend(s[j] - '0');
        }
        for(int i = 1; i <= n; ++i)
        {
            scanf("%s", s + 1);
            int len = strlen(s + 1), u = root, step = 0;
            for(int j = 1; j <= len; ++j) 
            {
                if(t[u].ch[s[j] - '0']) u = t[u].ch[s[j] - '0'], ++step;
                else
                {
                    while(u && !t[u].ch[s[j] - '0']) u = t[u].par;
                    if(!u) u = root, step = 0;
                    else
                    {
                        step = t[u].val + 1;
                        u = t[u].ch[s[j] - '0'];
                    }   
                }
                val[j] = step;
            }
            int l = 0, r = len + 1, ans = 0;
            while(r - l > 1) 
            {
                int mid = (l + r) >> 1;
                if(check(mid, len)) l = ans = mid;
                else r = mid;
            }
            printf("%d
    ", ans);
        } 
        return 0;
    }
    View Code
  • 相关阅读:
    .NET WinForm下StatusStrip控件如何设置分隔线及部分子控件右对齐
    winform 弹出窗体指定位置
    命名空间“System.Web”中不存在类型或命名空间名称“Optimization”(是否缺少程序集引用?)
    SQL之case when then用法详解
    VS2015编译错误:调用的目标发生了异常--->此实现不是Windows平台FLPS验证的加密算法的一部分。
    .NET Core 控制台中文乱码问题!
    IOC 依赖注入 Unity
    《重构:改善代码的既有设计》读书笔记
    .NET中的堆(Heap)和栈(Stack)的本质
    浅谈 Sql Server 游标
  • 原文地址:https://www.cnblogs.com/19992147orz/p/7868165.html
Copyright © 2011-2022 走看看