zoukankan      html  css  js  c++  java
  • [CF149E] Martian Strings

    Description

    给定一个长度为 (n) 的主串 (S),有 (m le 100) 个询问,每次给定一个询问串 (p_i),长度不超过 (10^3)。输出有多少个询问串,满足存在 (S) 的两个不相交的子串拼起来与 (p_i) 相等。

    Solution

    一个询问串可行,当且仅当存在 (k) 使得 (p[1..k]) 在主串中的最小结束位置 (pre[k]) 小于 (p[k+1,l]) 在主串中的最大开始位置 (suf[k+1]),下面考虑如何求解 (pre[],suf[])

    (S) 的正串反串分别建立后缀自动机,预处理出正串自动机中每个结点对应的 (endpos) 集合的最小值,反串自动机中每个结点对应的 (endpos) 集合的最小值

    具体地,在增量构造时记录一个 (firstpos[p]) 起初就等于 (p) 的结尾位置(新建结点的时候设置一下,当结点被建立时是唯一的),在构造完毕后按照拓扑序沿着后缀树上传取 (min)(lastpos) 同理

    现在,将 (p) 的正串扔到 (S) 的正串自动机上跑,只走转移边不走后缀链接,假设跑到了 (p[1..i]) 那么我们就得到了 (pre[i]),同理得到 (suf[i])

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 400005;
    const int dbg = 0;
    
    int n,ans=0;
    string s,p;
    
    struct SAM
    {
        int len[N], ch[N][26], fa[N], ind, last;
        int t[N], a[N], cnt[N], f[N], fpos[N];
        SAM()
        {
            ind = last = 1;
        }
        inline void extend(int id, int pos)
        {
            int cur = (++ ind), p;
            len[cur] = len[last] + 1;
            cnt[cur] = 1;
            fpos[cur] = pos;
            for (p = last; p && !ch[p][id]; p = fa[p]) ch[p][id] = cur;
            if (!p) fa[cur] = 1;
            else
            {
                int q = ch[p][id];
                if (len[q] == len[p] + 1) fa[cur] = q;
                else
                {
                    int tmp = (++ ind);
                    len[tmp] = len[p] + 1;
                    for(int i=0; i<26; i++) ch[tmp][i] = ch[q][i];
                    fa[tmp] = fa[q];
                    fpos[tmp] = fpos[q];
                    for (; p && ch[p][id] == q; p = fa[p]) ch[p][id] = tmp;
                    fa[cur] = fa[q] = tmp;
                }
            }
            last = cur;
        }
        void calc()
        {
            memset(t, 0, sizeof t);
            for(int i=1; i<=ind; i++) t[len[i]]++;
            for(int i=1; i<=ind; i++) t[i]+=t[i-1];
            for(int i=1; i<=ind; i++) a[t[len[i]]--]=i;
            for(int i=ind; i>=1; --i) cnt[fa[a[i]]]+=cnt[a[i]];
            cnt[1] = 0;
        }
        void solve_min()
        {
            for(int i=ind; i>=1; --i) fpos[fa[a[i]]]=min(fpos[fa[a[i]]],fpos[a[i]]);
        }
        void solve_max()
        {
            for(int i=ind; i>=1; --i) fpos[fa[a[i]]]=max(fpos[fa[a[i]]],fpos[a[i]]);
        }
        void presolve(string s,int op)
        {
            for(int i=0;i<s.length();i++)
            {
                extend(s[i]-'A',i+1);
            }
            calc();
            if(op==0) solve_min();
            else solve_min();
        }
        void run(string s,int *a)
        {
            int p=1;
            for(int i=0;i<s.length();i++)
            {
                if(ch[p][s[i]-'A'])
                {
                    p=ch[p][s[i]-'A'];
                    a[i]=fpos[p];
                }
                else break;
            }
        }
    } sam_pre,sam_suf;
    
    int pre[1005],suf[1005];
    
    void solve()
    {
        cin>>p;
    
        memset(pre,0,sizeof pre);
        memset(suf,0,sizeof suf);
    
        int m=p.length();
    
        sam_pre.run(p,pre+1);
        reverse(p.begin(),p.end());
        sam_suf.run(p,suf+1);
        reverse(p.begin(),p.end());
        reverse(suf+1,suf+m+1);
        for(int i=1;i<=m;i++) suf[i]=s.length()-suf[i]+1;
    
        if(dbg)
        {
            cout<<"string="<<p<<endl;
            for(int i=1;i<=m;i++)
            {
                cout<<" i="<<i<<" pre="<<pre[i]<<" suf="<<suf[i]<<endl;
            }
            cout<<endl;
        }
    
        int flag=0;
        for(int i=1;i<m;i++)
        {
            if(pre[i] && suf[i+1]<=s.length() && pre[i]<suf[i+1])
            {
                flag=1;
                if(dbg) cout<<" succeed : i="<<i<<endl;
            }
        }
    
        ans+=flag;
    }
    
    signed main()
    {
        ios::sync_with_stdio(false);
    
        cin>>s;
        cin>>n;
        sam_pre.presolve(s,0);
        reverse(s.begin(),s.end());
        sam_suf.presolve(s,1);
        reverse(s.begin(),s.end());
    
        for(int i=1;i<=n;i++) solve();
    
        cout<<ans<<endl;
    }
    
    
  • 相关阅读:
    小猫爬山
    方格取数
    Bash 脚本编程
    树链剖分学习笔记
    输入输出优化
    没有上司的舞会
    线性dp 之 麻烦的聚餐
    飞翔
    挖地雷
    The Prices
  • 原文地址:https://www.cnblogs.com/mollnn/p/13579835.html
Copyright © 2011-2022 走看看