题意是给出n个字符串,有Q组询问,每次询问第x和第y个字符串的最长公共子串
感觉这题会后缀自动机就挺水了…
因为字符串总长<=1e5,所以第一反应就可以分块
首先对每个串建后缀自动机
因为后缀自动机可以O(串长)求出两个串的最长公共子串,所以如果询问的两个串有一个串长小于sqrt(总串长),那么直接在较长串的后缀自动机上查询就可以了。
那如果有两个串都大于sqrt(总串长)呢
依然可以暴力查询,只不过记忆化一下,这样时间复杂度就对了…
因为最坏情况需要
∑numi=1∑numj=i+1min(ai,aj)
其中num为长度大于sqrt(总串长)的字符串个数,ai表示第i个字符串的长度
把字符串按串长排序
上面的式子推一下就变成
∑numi=1ai×(num−i)
so复杂度就是
num×∑numi=1ai
因为num是长度大于sqrt(总长)的字符串的个数,所以是根号级别的,后面的sigma是O(n)级别的,所以这样子复杂度就
O(nn√)
了
就是常数有点大
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
#include <ctime>
using namespace std;
const int N=100010;
int n,tot,m,q;
int x[N],len[N],f[N],ib[N],rt[N];
char a[N*3];
vector<int> b,s;
int nxt[N*3][30],stp[N*3],fail[N*3],p[N],cnt;
int ans[320][320];
inline void fix(int &x,int y){
if(x<y) x=y;
}
inline void extend(int rt,int &p,int x){
int np=++cnt; stp[np]=stp[p]+1;
while(!nxt[p][x]&&p) nxt[p][x]=np,p=fail[p];
if(!p) fail[np]=rt;
else{
int q=nxt[p][x];
if(stp[q]==stp[p]+1) fail[np]=q;
else{
int nq=++cnt; stp[nq]=stp[p]+1;
fail[nq]=fail[q];
memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
fail[q]=fail[np]=nq;
while(nxt[p][x]==q&&p) nxt[p][x]=nq,p=fail[p];
}
}
p=np;
}
inline int calc(const int &u,const int &v){
int cur=rt[v],ret=0,ans=0;
for(int i=x[u];i<x[u]+len[u];i++){
if(nxt[cur][a[i]-'a']){
ret++; cur=nxt[cur][a[i]-'a'];
fix(ans,ret);
continue;
}
while(!nxt[cur][a[i]-'a']&&cur) cur=fail[cur];
if(!cur) ret=0,cur=rt[v];
else{
ret=stp[cur]+1;
fix(ans,ret);
cur=nxt[cur][a[i]-'a'];
}
}
return ans;
}
int main(){
scanf("%d%d",&n,&q); int lst=0;
for(int i=1;i<=n;i++){
scanf("%s",a+lst+1);
x[i]=lst+1; len[i]=strlen(a+lst+1);
lst+=len[i]; tot+=len[i];
}
m=sqrt(tot);
for(int i=1;i<=n;i++)
(len[i]<=m?s:b).push_back(i);
for(int i=1;i<=n;i++){
rt[i]=p[i]=++cnt;
for(int j=0;j<len[i];j++)
extend(rt[i],p[i],a[x[i]+j]-'a');
}
for(int i=0;i<b.size();i++)
f[b[i]]=i,ib[b[i]]=1;
while(q--){
int u,v; scanf("%d%d",&u,&v); u++; v++;
if(ib[u]&&ib[v]){
if(len[u]>len[v]) swap(u,v);
if(!ans[f[u]][f[v]])
ans[f[u]][f[v]]=ans[f[v]][f[u]]=calc(u,v);
printf("%d\n",ans[f[u]][f[v]]);
}
else{
if(ib[u]) swap(u,v);
int cur=rt[v],ret=0,ans=0;
printf("%d\n",calc(u,v));
}
}
return 0;
}