String
String
题解
还是因为某些原因,这里用英文名。
首先一看到找最长公共子串,我们就想到了后缀自动机。
我们先对每个字符串,建一棵SAM。
对于每个询问,就将两棵SAM进行匹配,由于每次匹配实际上是将两个DAG匹配,由于是DAG,我们最好记忆化一下。
每次匹配的时间复杂度大概是
O
(
α
s
i
z
(
较
小
子
树
)
)
O\left(\alpha siz(较小子树)\right)
O(αsiz(较小子树))。
由于总字符串长度与
n
n
n同阶,我们总时间复杂度为
O
(
α
q
n
)
O\left(\alpha q\sqrt{n}\right)
O(αqn)。
现在,你将这个暴力直接交上去只会有68pts。
考虑对
α
\alpha
α进行优化,这个
a
l
p
h
a
alpha
alpha包含两个方面,一是记忆化,二是枚举下一个转移。
记忆化我们可以采用unordered_map,对于转移又该如何优化呢?
我们知道,大部分
S
A
M
SAM
SAM上的点能够转移的点是比较少的,匹配两个点时,我们是针对它们都有的边进行转移。一个点的总出边不会超过
26
26
26条,考虑状压。
但我们发现
2
26
=
67108864
2^{26}=67108864
226=67108864,明显会MLE。
我们可以用类似链向星的形式,记录下一个可转移的点,当两边一样是再匹配。
当我们这样对
α
\alpha
α优化后,还是只有76pts,考虑对其它地方进行优化。
复杂度基本无法优化了,想想出题人会怎样造数据。
能卡人的数据一定是长串与短串特别分明,即长串尽可能的多。
于是,短串能得到的长度就特别的小,而总共又只有26个字母。
所以能卡人的数据为了让人尽可能地向下,一定会有很多相同的串。
于是,我们可以将相同的串hash去掉,再进行询问。
现在,你就成功卡过了。
这又是什么奇技淫巧呀!
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<unordered_map>
using namespace std;
#define MAXN 160005
#define MAXM 50005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
const LL mod1=1e9+9;
const LL mod2=998244353;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,q,tot,id[MAXM],leng[MAXM];char str[MAXM];
unordered_map<LL,int> S;
unordered_map<LL,int> Hs;
struct ming{int ch[27],fa,len,nxt[27],head;}a[MAXN];
struct SAM{
int root,las,sz;
void init(){las=root=++tot;}
void extend(const int x){
int p=las,np=las=++tot;a[np].len=a[p].len+1;
for(;p&&!a[p].ch[x];p=a[p].fa)a[p].ch[x]=np;
if(!p){a[np].fa=root;return ;}int q=a[p].ch[x];
if(a[q].len==a[p].len+1){a[np].fa=q;return ;}
int nq=++tot;a[nq]=a[q];a[nq].len=a[p].len+1;a[q].fa=a[np].fa=nq;
for(;p&&a[p].ch[x]==q;p=a[p].fa)a[p].ch[x]=nq;
}
void solve(){
for(int i=root;i<=tot;++i){
int now=26;
for(int j=25;j>=0;--j){
a[i].nxt[j]=now;
if(a[i].ch[j])now=j;
}
a[i].head=now;
}
sz=tot-root+1;
}
}T[MAXM];
int sakura(const int u,const int v){
const LL S1=1ll*u*tot+1ll*v;
if(S.find(S1)!=S.end())return S[S1];int res=0;
for(int i=max(a[u].head,a[v].head);i<26;){
if(a[u].ch[i]&&a[v].ch[i])
res=max(res,sakura(a[u].ch[i],a[v].ch[i])+1);
i=max(a[u].nxt[i],a[v].nxt[i]);
}
S[S1]=res;return res;
}
signed main(){
read(n);read(q);int sum=0;
for(int i=1;i<=n;i++){
T[i].init();scanf("%s",str+1);int len=strlen(str+1),now1=0,now2=0;leng[i]=len;id[i]=i;
for(int j=1;j<=len;j++)now1=(233ll*now1+1ll*str[j])%mod1,now2=(233ll*now2+1ll*str[j])%mod2;
LL tmp=1ll*now1*mod2+1ll*now2;if(Hs[tmp]){id[i]=Hs[tmp];continue;}else Hs[tmp]=i;
for(int j=1;j<=len;j++)T[i].extend(str[j]-'a');T[i].solve();sum+=len;
}
for(int i=1;i<=q;i++){
int u,v,ans;read(u);read(v);u++;v++;u=id[u];v=id[v];
if(u==v){printf("%d\n",leng[u]);continue;}
if(T[u].sz>T[v].sz)swap(u,v);u=T[u].root;v=T[v].root;
ans=sakura(v,u);printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号