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