[十二省联考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
#include
#include
#include
#include
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 G[2*N];
int Glas[2*N];
queue 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]=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());
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(字符串,图论,动态规划,c++,算法,字符串,动态规划,图论)