[十二省聯考2019]字符串問題 (SAM優化建圖+DAG上DP)

題面見:https://www.luogu.com.cn/problem/P5284

 

 

題解

當年考的時候直接寫了40暴力。。。

現在看了看,好像可以用後綴樹優化建圖

先倒着建一個SAM,然後再倍增定位每個區間

後綴樹上的邊就從父親連向兒子,A連邊向B

此時我們本來應該讓B向其定位的區間連邊的

但是一個點可能會對應多個區間,直接連邊會出很多其他的問題

於是我們換一種思路,把定位在同一個點的區間按照長度排序,B排在A的前面

先從SAM上的點依次向這些點連邊,遇到了B之後就再從B開始向後連邊,這樣就保證了連邊的數量以及連通性

注意,向兒子連邊時要用當前點的最後一個B來連邊,如果直接用當前點來連邊會導致後面的B區間無法與兒子連通

這種寫法會好寫很多

如果建出來的圖有環就說明可以一直轉圈得到無窮大的答案

如果不存在環就是一個DAG

最後DAG上DP求最大權路徑的時候只把A點的權值算進去就可以了

本蒟蒻的第二道字符串大題。。。

代碼:(雖然只有2.6KB,我也不知道爲什麼寫了這麼久。。。我太菜了)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 200005
#define LOG 19
#define LL long long
int all;
int fir[4*N],to[5*N],nxt[5*N],ind[4*N],cnt;
char ss[N];
int fa[2*N],ch[2*N][26],len[2*N],las,tot;
int pos[2*N],f[LOG+1][2*N];
int tp[4*N];
vector<int> G[2*N];
int Glas[2*N];
queue<int> q;
LL dp[4*N];
void csh()
{
	for(int i=1;i<=all;i++)
		dp[i]=fir[i]=tp[i]=ind[i]=0;
	for(int i=1;i<=tot;i++){
		G[i].clear();
		fa[i]=len[i]=pos[i]=Glas[i]=0;
		for(int j=0;j<=LOG;j++)f[j][i]=0;
		for(int j=0;j<26;j++)ch[i][j]=0;
	}
	las=tot=1;cnt=all=0;
}
void adde(int a,int b)
{
	//printf("adde:%d %d\n",a,b);
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	ind[b]++;
}
void extend(int x)
{
	int p,np,q,nq;
	p=las;las=np=++tot;
	len[np]=len[p]+1;
	for(;p&&!ch[p][x];p=fa[p])ch[p][x]=np;
	if(!p)fa[np]=1;
	else{
		q=ch[p][x];
		if(len[q]==len[p]+1)fa[np]=q;
		else{
			nq=++tot;
			len[nq]=len[p]+1;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));fa[nq]=fa[q];
			for(;p&&ch[p][x]==q;p=fa[p])ch[p][x]=nq;
			fa[q]=fa[np]=nq;
		}
	}
}
void add(bool flg)
{
	int l=gi(),r=gi()-l+1;l=pos[l];
	for(int i=LOG;i>=0;i--)if(len[f[i][l]]>=r)l=f[i][l];
	tp[++all]=flg;len[all]=r;
	G[l].push_back(all);
}
bool cmp(int x,int y){return len[x]<len[y]||(len[x]==len[y]&&tp[x]<tp[y]);}
LL solve()
{
	for(int i=1;i<=all;i++)if(!ind[i])q.push(i);
	int u;LL ret=0;
	while(!q.empty()){
		u=q.front();q.pop();
		ret=max(ret,dp[u]+len[u]);
		for(int v,p=fir[u];p;p=nxt[p]){
			v=to[p];
			dp[v]=max(dp[v],dp[u]+len[u]);
			ind[v]--;if(!ind[v])q.push(v);
		}
	}
	for(int i=1;i<=all;i++)if(ind[i])return -1;
	return ret;
}
int main()
{
	int T,n,na,nb,m,i,j,u,v;
	T=gi();
	while(T--){
		csh();
		scanf("%s",ss+1);
		n=strlen(ss+1);
		for(i=n;i>=1;i--){extend(ss[i]-'a');pos[i]=las;}
		for(i=1;i<=tot;i++)f[0][i]=fa[i];
		for(j=1;j<=LOG;j++)for(i=1;i<=tot;i++)f[j][i]=f[j-1][f[j-1][i]];
		all=tot;
		na=gi();for(i=1;i<=na;i++)add(1);
		nb=gi();for(i=1;i<=nb;i++)add(0);
		for(i=1;i<=tot;i++){
			u=i;sort(G[i].begin(),G[i].end(),cmp);
			for(j=0;j<(int)G[i].size();j++){
				v=G[i][j];adde(u,v);
				if(!tp[v])u=v;// OAA+BAA+BAA+BAA...
			}
			Glas[i]=u;// the last B
		}
		for(i=1;i<=all;i++)if(!tp[i])len[i]=0;
		for(i=2;i<=tot;i++)adde(Glas[fa[i]],i);//.....BAA+son
		m=gi();for(i=1;i<=m;i++){u=gi()+tot;v=gi()+tot+na;adde(u,v);}
		printf("%lld\n",solve());
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章