一开始有两个字符串集合 S,T ,里面都只有一个空串。
有 5 种操作
1. 在 S 中选一个字符串 Si ,新建一个字符串 Snew=Si+c , c 是一个小写字母,然后把 Snew 塞进 S 中。
2. 在 T 中选一个字符串 Ti ,新建一个字符串 Tnew=Ti+c , c 是一个小写字母,然后把 Tnew 塞进 T 中。
3. 在 T 中选一个字符串 Ti ,新建一个字符串 Tnew=c+Ti , c 是一个小写字母,然后把 Tnew 塞进 T 中。
4. 在 T 中选两个字符串 Ti,Tj , i 可以等于 j .然后把 Ti+Tj 塞入 T 中。
5. 在 S 中选出 Si , T 中选出 Tj .询问 Tj 在 Si 的出现次数。
首先要离线。注意到 S 其实就是一棵 Trie .我们对这棵 Trie 建出他的后缀数组。
为了方便,我们需要把所有的 Si,Ti 都反序。那么 Trie 上的后缀只有 O(N) 个。
那考虑如何对Trie上的串的反串进行后缀排序。
其实很简单。我们只需要对Trie建出其SAM。然后SAM的fail树就是反串的后缀树。再进行一次DFS就排好序啦!!(说的好简单。。其实不好实现。具体要看代码。。)
既然如此,我们之后考虑的所有的 Suffixj 都是已经排好序的了。
我们对于 T 中所有的字符串 Ti 维护一个区间 [Li,Ri] ,表示
∀j∈[Li,Ri]LCP(Ti,Suffixj)=|Ti| .且这个区间是极大的。我们可能不存在这个区间。计为 [−1,−1] 即可。
注意到 2,3 操作都只是 4 操作的特例。那我们就只用考虑 4 操作即可。
假设现在的新串为 p=a+b ,两个区间为 [La,Ra],[Lb,Rb] .假如有一个不存在区间的话,那么这个新串肯定也是不存在的。
由于 a 为 p 的前缀。所以 [Lp,Rp]⊂[La,Ra] .
要求得 [Lp,Rp] ,其实相当于求出最大的 l 使得 Sufl<p ,最小的 r 使得 Sufr>p .并且这里的 > 是指 Sufr 的前 |p| 位要比 p 大。然后 Lp=l+1,Rp=r−1 .
那么我们可以二分出 l,r .因为我们已经把后缀排好序了。
当然 l∈[La,Ra],r∈[La,Ra]
现在要比较 Sufl 与 p 的大小。因为 l∈[La,Ra] ,所以 Sufl 的前缀必为 a .那么我们只需要比较 Sufl 除掉 a 的部分与 b 的大小就好了。因为一个后缀的后缀也是一个后缀,那我们直接用 Rank[Sufk] 与 Lb,Rb 比较即可。 k 是 l 跳了 |a| 步后的后缀,也就相当于在 Trie 上的第 |a| 个祖先。
那么我们就需要一个高效的求第 K 个祖先的做法。
单纯的倍增是 O(logN) 的。但是常数较大。所以我们需要一个常数小一点的做法。
比如说,把原来的二进制改为一个p进制。为了方便,不妨取 p=32 ,那么我们一个数字 n,n≤100000 的32进制不会超过4位。
我们存一个数组 Ancp,i,j ,表示 p 这个点向上跳 j∗32i 步的祖先是谁。这个可以预处理出来。时间复杂度是 O(N∗32∗4) .
然后每次询问的时候,我们就把 K 设为 p∗323+p1∗322+p2∗32+p3 .只需要四次迭代就可以计算出第 K 个祖先了!!
有了这个算法之后,我们对于拼接两个字符串并确定出他的区间就有了一个 O(logN) .
那么我们最后就只需要考虑怎么样统计一个字符串 Ti 在 Sj 中的出现次数了。
因为我们已经知道了 [Li,Ri] ,那么事实上 Ti 在 Sj 的出现次数,相当于 Ti 被多少个 Sj 的后缀覆盖了。那么我们就只需要统计一下
有多少 k∈[Li,Ri]∧k∈Ancj .
那我们可以将这个询问挂在 Trie 上的 j 点上,然后边递归边统计就好了。
总的时间复杂度是 O(NlogN) 了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 300005;
struct TrieInfo
{
int To[26],Fa[4][32],ref,l;
};
struct Match
{
int l,r,len;
Match(void){}
Match(int a,int b,int c) : l(a),r(b),len(c){}
}Info[MAXN],Map[26];
int Sa[MAXN],Rank[MAXN],Ti[MAXN],Cnt;
int Final[MAXN],Next[MAXN],total,Ans[MAXN];
namespace Trie
{
const int MAXN = 100005;
TrieInfo T[MAXN];
int Trans[MAXN],A[MAXN],cnt;
void First()
{
cnt = 1;
Trans[1] = -1;
}
int Insert(int cur,char c)
{
c = c - 'a';
if (T[cur].To[c]) return T[cur].To[c];
T[++ cnt].Fa[0][1] = cur;
T[cnt].l = T[cur].l + 1;
Trans[cnt] = c;
return (T[cur].To[c] = cnt);
}
int Getfa(int u,int dep)
{
int cur = u;
for(int i = 0;i < 4;i ++,dep >>= 5)
{
int p = (dep & 31);
if (!p) continue;
cur = T[cur].Fa[i][p];
}
return cur;
}
void Work()
{
for(int i = 2;i < 32;i ++)
for(int j = 1;j <= cnt;j ++) T[j].Fa[0][i] = T[T[j].Fa[0][i - 1]].Fa[0][1];
for(int i = 1;i < 4;i ++)
for(int j = 1;j < 32;j ++)
for(int k = 1;k <= cnt;k ++)
{
if (j == 1) T[k].Fa[i][j] = T[T[k].Fa[i - 1][31]].Fa[i - 1][1]; else
T[k].Fa[i][j] = T[T[k].Fa[i][j - 1]].Fa[i][1];
}
}
void Add(int p,int v)
{
for(;p <= Cnt;p += p & -p) A[p] += v;
}
int Sum(int p)
{
if (p < 0) return 0;
int tmp = 0;
for(;p;p -= p & -p) tmp += A[p];
return tmp;
}
int Sum(int l,int r)
{
if (l > r) return 0;
return Sum(r) - Sum(l - 1);
}
void Dfs(int Now)
{
Add(Rank[Now],1);
for(int j = Final[Now];j;j = Next[j])
{
Ans[j] = Sum(Info[Ti[j]].l,Info[Ti[j]].r);
if (!Info[Ti[j]].len) Ans[j] = 0;
}
for(int i = 0;i < 26;i ++)
if (T[Now].To[i])
Dfs(T[Now].To[i]);
Add(Rank[Now],-1);
}
void GetAns()
{
Dfs(1);
}
}
namespace SAM
{
struct Node
{
int To[26],fail,len,down,ref;
}T[MAXN];
int Go[MAXN][26];
int Q[MAXN],Ord[MAXN],S,cnt;
bool cmp(int a,int b)
{
return T[a].len < T[b].len;
}
int Add(int Lst,int c,int l,int cur)
{
int Nt = ++ cnt,p = Lst;
T[Nt].len = l;
for(;p && !T[p].To[c];p = T[p].fail) T[p].To[c] = Nt;
if (!p) T[Nt].fail = S; else
if (T[T[p].To[c]].len == T[p].len + 1) T[Nt].fail = T[p].To[c]; else
{
int q = ++ cnt,qt = T[p].To[c];
T[q] = T[qt];
T[q].len = T[p].len + 1;
T[qt].fail = T[Nt].fail = q;
for(;p && T[p].To[c] == qt;p = T[p].fail) T[p].To[c] = q;
}
return Nt;
}
void Dfs(int Now)
{
if (T[Now].ref) Sa[++ Cnt] = T[Now].ref,Rank[T[Now].ref] = Cnt;
for(int i = 0;i < 26;i ++)
if (Go[Now][i])
Dfs(Go[Now][i]);
}
void Mark(int tnod,int snod)
{
T[snod].down = T[snod].ref = tnod;
for(int i = 0;i < 26;i ++)
if (Trie::T[tnod].To[i])
Mark(Trie::T[tnod].To[i],T[snod].To[i]);
}
void Build()
{
int fi = 0,en = 1;
Q[1] = 1;
S = cnt = 1;
Trie::T[1].ref = 1;
T[1].ref = 1;
while (fi < en)
{
int u = Q[++ fi];
TrieInfo &c = Trie::T[u];
for(int i = 0;i < 26;i ++)
if (c.To[i])
{
TrieInfo &cur = Trie::T[c.To[i]];
cur.ref = Add(c.ref,i,cur.l,c.To[i]);
Q[++ en] = c.To[i];
}
}
Mark(1,1);
for(int i = 1;i <= cnt;i ++) Ord[i] = i;
sort(Ord + 1,Ord + cnt + 1,cmp);
for(;cnt > 1;cnt --)
{
int v = Ord[cnt],u = T[v].fail;
T[u].down = T[v].down;
TrieInfo &cur = Trie::T[T[v].down];
int k = Trie::Getfa(T[u].down,cur.l - (T[cur.ref].len - T[u].len));
Go[u][Trie::Trans[k]] = v;
}
Dfs(1);
}
}
struct Quer
{
int type,s,t;
char c;
}Q[MAXN];
int Refer[MAXN],Len[MAXN],N,M,Tc;
inline bool Small(int suf,const Match &a)
{
return Rank[suf] < a.l;
}
inline bool Large(int suf,const Match &a)
{
return Rank[suf] > a.r;
}
Match Merge(const Match &a,const Match &b)
{
if (a.l > a.r) return Match(0,-1,a.len + b.len);
int l = a.l,r = a.r,l1 = a.l - 1,r1 = a.r + 1,mid;
for(;l <= r;)
{
mid = l + r >> 1;
if (Small(Trie::Getfa(Sa[mid],a.len),b)) l1 = mid,l = mid + 1; else
r = mid - 1;
}
for(l = a.l,r = a.r;l <= r;)
{
mid = l + r >> 1;
if (Large(Trie::Getfa(Sa[mid],a.len),b)) r1 = mid,r = mid - 1; else
l = mid + 1;
}
return Match(l1 + 1,r1 - 1,a.len + b.len);
}
int main()
{
Trie::First();
N = 1;
Refer[1] = 1;
scanf("%d", &M);
for(int i = 1;i <= M;i ++)
{
int type,s,t;char c;
scanf("%d%d", &type, &s);
if (type == 1)
{
scanf(" %c", &c);
s = Refer[s];
Refer[++ N] = Trie::Insert(s,c);
}
if (type == 2)
{
scanf("%d %c", &t, &c);
Q[i].type = s + 1;Q[i].s = t;Q[i].c = c;
}
if (type == 3 || type == 4)
scanf("%d", &t),Q[i].type = type,Q[i].s = s,Q[i].t = t;
}
Trie::Work();
SAM::Build();
Tc = 1;
Info[1].l = 1,Info[1].r = Cnt;
for(int i = 0;i < 26;i ++)
{
int l = 1,r = Cnt,mid;
Map[i].r = Cnt + 1;
for(;l <= r;)
{
mid = l + r >> 1;
if (Trie::Trans[Sa[mid]] < i) Map[i].l = mid,l = mid + 1; else
r = mid - 1;
}
for(l = 1,r = Cnt;l <= r;)
{
mid = l + r >> 1;
if (Trie::Trans[Sa[mid]] > i) Map[i].r = mid,r = mid - 1; else
l = mid + 1;
}
Map[i].l ++,Map[i].r --;
Map[i].len = 1;
}
for(int i = 1;i <= M;i ++)
{
Quer &c = Q[i];
if (!c.type) continue;
if (c.type != 4) ++ Tc;
if (c.type == 4)
{
++ total;
c.t = Refer[c.t];
Next[total] = Final[c.t],Final[c.t] = total;
Ti[total] = c.s;
} else
{
//We must merge reversely.
if (c.type == 1) Info[Tc] = Merge(Info[c.s],Map[c.c - 'a']);
if (c.type == 2) Info[Tc] = Merge(Map[c.c - 'a'],Info[c.s]);
if (c.type == 3) Info[Tc] = Merge(Info[c.t],Info[c.s]);
}
}
Trie::GetAns();
for(int i = 1;i <= total;i ++) printf("%d\n", Ans[i]);
return 0;
}