CF427D Match & Catch 题解
虽然 \(n,m\leq 5000\),但是这题是 \(O((n+m)|\Sigma|)\) 的。首先建 \(S\) 的 SAM,拿 \(T\) 上去跑可以得到 \(T\) 每个前缀的匹配区间(在 \(S\) 中只出现一次)。接着建 \(T\) 的 SAM,也可以求出每个前缀在 \(T\) 中只出现一次的区间。做一个区间交,取一个全局 \(\min\) 即可。
点击查看代码
inline void clearvector(std::vector<int> &a){std::vector<int> b;std::swap(a,b);}
const int N=5000+13;
char s[N],t[N];
int nxt[N<<1],len[N<<1],ptot,lastpos,cnt[N<<1];
std::vector<int> son[N<<1],zrzak;
std::vector<int> G[N<<1];
inline void clear(){
for(int i=1;i<=ptot;++i) clearvector(son[i]),clearvector(G[i]),cnt[i]=nxt[i]=len[i]=0;
son[1].resize(26),zrzak.resize(26);
ptot=lastpos=1;
}
inline int newpos(std::vector<int> nson,int nlen){return len[++ptot]=nlen,std::swap(son[ptot],nson),ptot;}
inline void insert(int c){
int p=lastpos,u=newpos(zrzak,len[p]+1);cnt[u]=1;
while(p&&!son[p][c]) son[p][c]=u,p=nxt[p];
lastpos=u;
if(!p) return nxt[u]=1,void();
int d=son[p][c];
if(len[d]==len[p]+1) nxt[u]=d;
else{
int v=newpos(son[d],len[p]+1);
nxt[v]=nxt[d],nxt[u]=nxt[d]=v;
while(p&&son[p][c]==d) son[p][c]=v,p=nxt[p];
}
}
void dfs(int u){for(auto v:G[u])dfs(v),cnt[u]+=cnt[v];}
pii a[N];
int main(){
clear();
read(s+1),read(t+1);
int n=strlen(s+1),m=strlen(t+1);
for(int i=1;i<=n;++i) insert(s[i]-'a');
for(int i=2;i<=ptot;++i) G[nxt[i]].pb(i);
dfs(1);
for(int i=1,now=1,l=0;i<=m;++i){
int c=t[i]-'a';
while(now!=1&&!son[now][c]) now=nxt[now],l=len[now];
if(son[now][c]) now=son[now][c],++l;
if(l&&cnt[now]==1) a[i]=mp(len[nxt[now]]+1,l);
}
clear();
for(int i=1;i<=m;++i) insert(t[i]-'a');
for(int i=2;i<=ptot;++i) G[nxt[i]].pb(i);
dfs(1);
int ans=INF;
for(int i=1,now=1,l=0;i<=m;++i){
int c=t[i]-'a';
while(now!=1&&!son[now][c]) now=nxt[now],l=len[now];
if(son[now][c]) now=son[now][c],++l;
if(cnt[now]==1){
int l1=a[i].fi,r1=a[i].se,l2=len[nxt[now]]+1,r2=l;
if(l1>l2) swap(l1,l2),swap(r1,r2);
if(l2&&l2<=r1) chkmin(ans,l2);
}
}
if(ans==INF) ans=-1;
println(ans);
return 0;
}