#SAM,子序列自动机#洛谷 4112 [HEOI2015] 最短不公共子串
分析
实际上,无论是子串还是子序列,都可以转化为自动机上的转移,也就相当于在两个自动机上同时使用相同的边进行转移,
如果前者 a 可以转移而后者 b 无法转移,那么就能贡献答案;否则就会在自动机上无限转移,由于两个自动机状态都是 \(O(n)\) 级别的,
可使用广搜 \(O(n^2)\) 标记走过的状态实现。
代码
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=2011; char s[2][N]; int n[2],nxt[2][N][26],v[N<<1][N<<1],upd;
struct SAM{
int tot,nxt[N][26],fail[N],last,len[N];
int Insert(char ch,int last){
int now=++tot,p;
len[now]=len[last]+1;
for (p=last;p&&!nxt[p][ch-97];p=fail[p]) nxt[p][ch-97]=now;
if (!p) fail[now]=1;
else{
int q=nxt[p][ch-97];
if (len[p]+1==len[q]) fail[now]=q;
else{
int _q=++tot; len[_q]=len[p]+1,
memcpy(nxt[_q],nxt[q],sizeof(nxt[q]));
for (;p&&nxt[p][ch-97]==q;p=fail[p]) nxt[p][ch-97]=_q;
fail[_q]=fail[q],fail[q]=fail[now]=_q;
}
}
return now;
}
}sam[2];
struct rec{int x,y,d;};
void bfs(int nxt0[N][26],int nxt1[N][26],int st0,int st1){
queue<rec>q;
q.push((rec){st0,st1,0});
v[st0][st1]=++upd;
while (!q.empty()){
int x=q.front().x,y=q.front().y,d=q.front().d; q.pop();
for (int i=0;i<26;++i) if (nxt0[x][i]){
if (nxt1[y][i]){
int zx=nxt0[x][i],zy=nxt1[y][i];
if (v[zx][zy]!=upd){
v[zx][zy]=upd;
q.push((rec){zx,zy,d+1});
}
}else{
printf("%d\n",d+1);
return;
}
}
}
printf("-1\n");
}
signed main(){
scanf("%s",s[0]),n[0]=strlen(s[0]),sam[0].tot=1;
for (int i=0,p=1;i<n[0];++i) p=sam[0].Insert(s[0][i],p);
for (int i=n[0];i;--i){
memcpy(nxt[0][i-1],nxt[0][i],104);
nxt[0][i-1][s[0][i-1]-97]=i;
}
scanf("%s",s[1]),n[1]=strlen(s[1]),sam[1].tot=1;
for (int i=0,p=1;i<n[1];++i) p=sam[1].Insert(s[1][i],p);
for (int i=n[1];i;--i){
memcpy(nxt[1][i-1],nxt[1][i],104);
nxt[1][i-1][s[1][i-1]-97]=i;
}
bfs(sam[0].nxt,sam[1].nxt,1,1);
bfs(sam[0].nxt,nxt[1],1,0);
bfs(nxt[0],sam[1].nxt,0,1);
bfs(nxt[0],nxt[1],0,0);
return 0;
}

浙公网安备 33010602011771号