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);
}