SNOI2020 LOJ3326 字符串

题目传送门

分析:
(有生之年考场上会做的后缀数组题
(后缀数组是个好东西,我有头发的时候天天写

把两个串接在一起,中间隔一个分隔符
\(sa\)\(height\)跑出来
把我们所需要配对的子串首位置的\(rk\)位置标记
两个子串\(s1,s2\)匹配代价为\(K-lcp(s1,s2)\)
转化为区间\(height\)最小值
于是变成了这样一个问题。。
数轴上有红蓝两种点,两两匹配价值为两点之间单位线段权值的最小值
要求价值最大
做法很经典了,把线段从大到小排序,并查集合并计算就好了。。
wdnmd,\(height\)最小值没和\(K\)取min,竟然能混70分???
(出题人用脚造数据(划去)

哦好像后缀自动机也能做
反向广义后缀自动机建出来,两个子串配对价值为LCA的len
\(O(n)\)胡乱DP一顿(口胡)

代码是后缀数组

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<string>

#define maxn 300005
#define MOD 1000000007
#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,n,m,K;
int sa[maxn],rk[maxn],tp[maxn],tax[maxn],hght[maxn];
char s[maxn];
int mn[maxn][19],lg[maxn];
int f[maxn],sz1[maxn],sz2[maxn],id[maxn];
long long ans;
inline bool cmp(int x,int y){return hght[x]>hght[y];}
inline int find(int x){return f[x]==x?x:f[x]=find(f[x]);}

inline void Rsort()
{
	for(int i=0;i<=m;i++)tax[i]=0;
	for(int i=1;i<=n;i++)tax[rk[tp[i]]]++;
	for(int i=1;i<=m;i++)tax[i]+=tax[i-1];
	for(int i=n;i;i--)sa[tax[rk[tp[i]]]--]=tp[i];
}
inline bool check(int x,int y,int k)
{return tp[x]==tp[y]&&tp[x+k]==tp[y+k];}

inline void getsa()
{
	for(int i=1;i<=n;i++)rk[i]=s[i],tp[i]=i;
	m=127,Rsort();
	for(int p=1,w=1;p<n;w<<=1,m=p)
	{
		p=0;
		for(int i=n-w+1;i<=n;i++)tp[++p]=i;
		for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w;
		Rsort(),swap(rk,tp),rk[sa[1]]=p=1;
		for(int i=2;i<=n;i++)rk[sa[i]]=check(sa[i],sa[i-1],w)?p:++p;
	}
	int k=0;
	for(int i=1;i<=n;i++)
	{
		k=k?k-1:k;
		for(int j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
		hght[rk[i]]=k;
	}
}

inline int query(int l,int r)
{
	int k=lg[r-l+1];
	return min(mn[l][k],mn[r-(1<<k)+1][k]);
}

inline int getlcp(int x,int y)
{
	if(x>y)swap(x,y);
	if(x==y)return n-sa[x]+1;
	return query(x+1,y);
}

int main()
{
	for(int i=0;i<19;i++)lg[1<<i]=i;
	for(int i=1;i<maxn;i++)lg[i]=max(lg[i],lg[i-1]);
	N=n=getint(),K=getint();
	scanf("%s",s+1);s[n+1]='#';
	scanf("%s",s+n+2);
	n=strlen(s+1);
	getsa();
	for(int i=1;i<=n;i++)f[i]=id[i]=i;
	sort(id+1,id+n+1,cmp);
	for(int i=1;i<=n;i++)mn[i][0]=hght[i];
	for(int j=1;j<19;j++)for(int i=1;i+(1<<(j-1))<=n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
	for(int i=1;i<=N-K+1;i++)sz1[rk[i]]=1;
	for(int i=1;i<=N-K+1;i++)sz2[rk[i+N+1]]=1;
	for(int i=1;i<=n;i++)
	{
		int p=id[i];if(p==1)continue;
		int u=find(p-1),v=find(p);
		f[v]=u,sz1[u]+=sz1[v],sz2[u]+=sz2[v];
		int tmp=min(sz1[u],sz2[u]);
		ans+=1ll*tmp*(K-min(hght[p],K));
		sz1[u]-=tmp,sz2[u]-=tmp;
	}
	printf("%lld\n",ans);
}

posted @ 2020-06-29 16:11  Izayoi_Doyo  阅读(300)  评论(0编辑  收藏  举报