[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;
}

上一篇:java learning


下一篇:B - Power Strings