LOJ3049 「十二省联考 2019」字符串问题
题目传送门
分析:
最暴力的做法,A向可以控制的B连边,B向以它为前缀的A连边
然后拓扑排序求一条路径的最大值
复杂度\(O(n^2)\)
明显要优化连边,这里给出两种解法:
SAM优化建边:
这里要前缀关系,我们反向建SAM将其变成后缀关系
对于每个串\([l,r]\),我们先找到\(r\)结尾的前缀在SAM上的位置,通过Parent树上倍增找到\([l,r]\)所在的endpos集合的点
对于每个Parent树上的点,维护一个vector,保存在这个点上的A和B
同一个点上的A和B,先按len为关键字从小到大排序,再将B优先放前面,A优先放后面
每个点向它子串集合中最短的连边,B向比他长的并且比下一个B短的A连边,Parent树父亲集合中最长的串向儿子连边
再把控制关系连上去
然后直接跑拓扑排序,时空复杂度都会从\(O(n^2)\)变成\(O(n)\)
写起来细节挺多的有点恶心
后缀数组+主席树优化建边:
我们先用原串跑一次后缀数组
B是A的前缀只需要\(min_{i=rk[lB]+1}^{rk[lA]}height[i]\geq len[B]\)
我们对于每个B,满足上述关系的一定是一个包含\(lB\)的区间
这个可以二分+\(O(1)\)RMQ解决
然后线段树优化建边?不行
发现A的长度如果小于B就会出现问题
那就用主席树
把子串长度从大到小排序,一个一个加入再优化建边就可以了
然后把控制关系也加进去
跑拓扑排序,但是时空复杂度都是\(O(nlogn)\),很卡常
代码是第一种做法的:
(后缀数组是个好东西,我有头发的时候天天写)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<queue>
#include<map>
#define maxn 400005
#define INF 0x3f3f3f3f
using namespace std;
inline int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n,m,na,nb;
int fir[maxn<<2],nxt[maxn<<3],to[maxn<<3],cnt;
struct node{
int fa,nxt[26],len;
}t[maxn];
int lst,tot,cur;
char s[maxn];
int len[maxn<<2],Ed[maxn<<2],pos[maxn<<2];
int Id[maxn<<2],f[maxn][20],In[maxn<<2];
int A[maxn<<2],B[maxn<<2];
long long F[maxn<<2];
vector<int>g[maxn<<2];
inline bool cmp(int x,int y){return len[x]==len[y]?pos[x]<pos[y]:len[x]<len[y];}
inline void insert(int c)
{
int p=lst,np=lst=++tot;
t[np].len=t[p].len+1;
while(p&&!t[p].nxt[c])t[p].nxt[c]=np,p=t[p].fa;
if(!p)t[np].fa=1;
else
{
int q=t[p].nxt[c];
if(t[q].len==t[p].len+1)t[np].fa=q;
else
{
int nq=++tot;
memcpy(t[nq].nxt,t[q].nxt,sizeof t[q].nxt);
t[nq].fa=t[q].fa,t[nq].len=t[p].len+1;
t[q].fa=t[np].fa=nq;
while(p&&t[p].nxt[c]==q)t[p].nxt[c]=nq,p=t[p].fa;
}
}
}
inline void solve(int val)
{
int l=getint(),r=getint();
r=r-l+1,l=Id[l];
for(int j=19;~j;j--)if(f[l][j]&&t[f[l][j]].len>=r)l=f[l][j];
pos[++cur]=val,len[cur]=r,g[l].push_back(cur);
}
inline void newnode(int u,int v)
{to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt,In[v]++;}
inline void toposort()
{
queue<int>Q;
for(int i=1;i<=cur;i++){F[i]=len[i];if(!In[i])Q.push(i);}
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=fir[u];i;i=nxt[i])
{
F[to[i]]=max(F[to[i]],F[u]+len[to[i]]);
if(!(--In[to[i]]))Q.push(to[i]);
}
}
}
int main()
{
int T=getint();
while(T--)
{
lst=tot=1;
memset(t,0,sizeof t);
memset(fir,0,sizeof fir),cnt=0;
memset(In,0,sizeof In),memset(pos,0,sizeof pos);
scanf("%s",s+1);n=strlen(s+1);
for(int i=n;i;i--)insert(s[i]-'a'),Id[i]=lst;
for(int i=1;i<=tot;i++)f[i][0]=t[i].fa;
for(int j=1;j<20;j++)for(int i=1;i<=tot;i++)f[i][j]=f[f[i][j-1]][j-1];
na=getint();cur=tot;
for(int i=1;i<=na;i++)solve(1),A[i]=cur;
nb=getint();
for(int i=1;i<=nb;i++)solve(0),B[i]=cur;
for(int i=1;i<=tot;i++)
{
sort(g[i].begin(),g[i].end(),cmp);
int pre=i;
for(int j=0;j<g[i].size();j++)
{
newnode(pre,g[i][j]);
if(!pos[g[i][j]])pre=g[i][j];
}
Ed[i]=pre;
}
for(int i=1;i<=tot;i++)g[i].clear();
for(int i=2;i<=tot;i++)newnode(Ed[t[i].fa],i);
for(int i=1;i<=cur;i++)if(!pos[i])len[i]=0;
m=getint();
for(int i=1;i<=m;i++)
{
int u=getint(),v=getint();
newnode(A[u],B[v]);
}
toposort();
bool flg=0;
for(int i=1;i<=cur;i++)if(In[i]){flg=1;break;}
if(flg)printf("-1\n");
else printf("%lld\n",*max_element(F+1,F+cur+1));
}
}