题意:
给你一个长度为
n
n
n的串,我们会从中选出
n
a
na
na个子串作为
A
A
A集合的串,选出
n
b
nb
nb个子串作为
B
B
B集合的串。然后会有
m
m
m组支配关系,每组支配关系给你一个在
A
A
A集合中的子串的编号,一个在
B
B
B集合中的子串编号,表示如果
B
B
B对应的这个编号的子串是某一个
A
A
A中的串的前缀的话,那个
A
A
A中的串就可以接在当前
A
A
A中的串的后面。问最长能接出多长的串,如果能无限长输出
−
1
-1
−1。多组数据。所有数据范围都是
2
e
5
2e5
2e5,字符集大小是小写字母。
题解:
送我退役的几道题中送的最彻底的一道。考场上想出了正解,却差一个排序没写出来。
来详细的讲述一下这个题的思考过程吧。首先一个问题是,我不怎么会SA,于是拿出一个子串的话只会SAM上子串定位,那么就来考虑一下SAM能不能做。我们子串定位的时候在定位的时候每个点挂一个vector,因为这个点可能是endpos等价类相同的点可能代表多个长度不同的串,所以我们还要按照长度顺序来连接这些点。考场上就是这个地方出了问题,没写出来的。
我们要子串定位之后找串之间的前缀关系,于是我们应该去建反串,这样parent树上的父节点都是子节点在原串上前缀。我们发现我们应该建图来求最大值。我们发现其实对于一个 B B B中的串,可以以它为前缀的 A A A中的串都是子串定位后它parent树中子树的A串。
往一个子树中的连边这个过程我们要想办法优化。这个优化的方法是我们利用parent树来优化。具体来讲,我们从父节点向子节点连边。然后我们考虑同一个点的vector之内的这些点,我们要保证不同长度的 B B B连的 A A A的集合不同,反正自己想个能保证这个的写法就行了。
然后你拓扑排序一下,看看是不是有环,有环的话就可以无限长,否则就一边拓扑排序一边求个最长路就好了。
这么看来也不是那么难嘛(虽然确实不怎么难啊QAQ)。时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)。
代码:
#include <bits/stdc++.h>
using namespace std;
int T,na,nb,n,m,cnt,rt=1,lst,fa[400010],ch[400010][26],len[400010];
int hed[400010],book[400010],f[400010][21],dep[400010],ed[800010];
int num,du[1600010],hed1[1600010],num1;
long long dis[1600010],ans,res[1600010];
char s[200010];
struct qwq
{
int len,opt,id;
};
bool operator < (qwq x,qwq y)
{
if(x.len==y.len)
return x.opt>y.opt;
return x.len>y.len;
}
vector<qwq> v[400010];
queue<int> q;
struct node
{
int to,next;
}a[3200010],b[3200010];
inline int read()
{
int x=0;
char s=getchar();
while(s>'9'||s<'0')
s=getchar();
while(s>='0'&&s<='9')
{
x=x*10+s-'0';
s=getchar();
}
return x;
}
inline void insert(int x,int id)
{
int cur=++cnt,pre=lst;
lst=cur;
len[cur]=len[pre]+1;
for(;!ch[pre][x]&⪯pre=fa[pre])
ch[pre][x]=cur;
if(!pre)
fa[cur]=rt;
else
{
int ji=ch[pre][x];
if(len[ji]==len[pre]+1)
fa[cur]=ji;
else
{
int gg=++cnt;
fa[gg]=fa[ji];
memcpy(ch[gg],ch[ji],sizeof(ch[ji]));
fa[ji]=fa[cur]=gg;
len[gg]=len[pre]+1;
for(;ch[pre][x]==ji&⪯pre=fa[pre])
ch[pre][x]=gg;
}
}
book[id]=cur;
}
inline void add(int from,int to)
{
a[++num].to=to;
a[num].next=hed[from];
hed[from]=num;
}
inline void add2(int from,int to)
{
b[++num1].to=to;
b[num1].next=hed1[from];
hed1[from]=num1;
}
inline void dfs(int x)
{
for(int i=1;i<=20;++i)
f[x][i]=f[f[x][i-1]][i-1];
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(y==f[x][0])
continue;
f[y][0]=x;
dep[y]=dep[x]+1;
dfs(y);
}
}
inline int lca(int x,int y)
{
for(int i=20;i>=0;--i)
{
if(f[x][i]&&len[f[x][i]]>=y)
x=f[x][i];
}
return x;
}
int main()
{
T=read();
while(T--)
{
cnt=1;
rt=1;
lst=1;
num=0;
num1=0;
scanf("%s",s+1);
n=strlen(s+1);
for(int i=n;i>=1;--i)
insert(s[i]-'a',i);
for(int i=2;i<=cnt;++i)
add(fa[i],i);
dep[1]=1;
dfs(1);
na=read();
for(int i=1;i<=na;++i)
{
int x=read(),y=read();
int z=lca(book[x],y-x+1);
dis[i+cnt]=y-x+1;
qwq ymh;
ymh.len=y-x+1;
ymh.opt=1;
ymh.id=i+cnt;
v[z].push_back(ymh);
}
nb=read();
for(int i=1;i<=nb;++i)
{
int x=read(),y=read();
int z=lca(book[x],y-x+1);
qwq ymh;
ymh.len=y-x+1;
ymh.opt=0;
ymh.id=i+cnt+na;
v[z].push_back(ymh);
}
for(int x=1;x<=cnt;++x)
{
int ji=x;
sort(v[x].begin(),v[x].end());
for(int i=v[x].size()-1;i>=0;--i)
{
int cur=v[x][i].id;
add2(ji,cur);
++du[cur];
if(!v[x][i].opt)
ji=cur;
}
ed[x]=ji;
}
for(int i=2;i<=cnt;++i)
{
add2(ed[fa[i]],i);
du[i]++;
}
m=read();
for(int i=1;i<=m;++i)
{
int x=read(),y=read();
add2(x+cnt,y+na+cnt);
du[y+na+cnt]++;
}
for(int i=1;i<=na+nb+cnt;++i)
{
if(!du[i])
{
res[i]=dis[i];
q.push(i);
}
}
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=hed1[x];i;i=b[i].next)
{
int y=b[i].to;
if(res[y]<res[x]+dis[y])
res[y]=res[x]+dis[y];
--du[y];
if(!du[y])
q.push(y);
}
}
int pd=0;
ans=0;
for(int i=1;i<=na+nb+cnt;++i)
{
if(du[i])
{
pd=1;
break;
}
ans=max(ans,res[i]);
}
if(pd==1)
printf("-1\n");
else
printf("%lld\n",ans);
for(int i=1;i<=na+nb+cnt;++i)
{
du[i]=0;
res[i]=0;
dis[i]=0;
ed[i]=0;
hed1[i]=0;
}
for(int i=1;i<=cnt;++i)
{
fa[i]=0;
len[i]=0;
hed[i]=0;
v[i].clear();
for(int j=0;j<=25;++j)
ch[i][j]=0;
}
for(int i=1;i<=n;++i)
{
book[i]=0;
s[i]=0;
}
}
return 0;
}