P4112 [HEOI2015] 最短不公共子串、
题目描述
在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。
下面给出一些定义:
- 一个串的“子串”指的是它的连续的一段,例如
bcd
是abcdef
的子串,但bde
不是。 - 一个串的“子序列”指的是它的可以不连续的一段,例如
bde
是abcdef
的子串,但bdd
不是。
下面,给两个小写字母串 \(a, b\),请你计算:
- \(a\) 的一个最短的子串,它不是 \(b\) 的子串。
- \(a\) 的一个最短的子串,它不是 \(b\) 的子序列。
- \(a\) 的一个最短的子序列,它不是 \(b\) 的子串。
- \(a\) 的一个最短的子序列,它不是 \(b\) 的子序列。
数据规模与约定
- 对于 \(100\%\) 的数据,保证 \(a\) 和 \(b\) 的长度都不超过 \(2000\)。
Solution:
首先,我们不仅要构造后缀自动机,还要构造一个叫做序列自动机的东西,构造方法也十分简单,我们只需要记录每个字符最后一次出现的位置 \(last_c\) 每次插入一个的时候枚举所有字符 \(p=last_i\) ,然后不断的跳 \(p=fa_p\) 直到其出现过 \(ch[p][c]\)
然后在这两个自动机上面同步跑 bfs 就好了,至于四遍分别这么跑详见代码。
Code:
#include<bits/stdc++.h>
const int N=4005;
using namespace std;
struct SAM{int ch[N][26],fa[N],len[N];int last,cnt;void init(){last=cnt=1;}void insert(int c){int p=last,q=++cnt;last=q;len[q]=len[p]+1;for(;p&&!ch[p][c];p=fa[p])ch[p][c]=q;if(!p){fa[q]=1;return ;}int x=ch[p][c];if(len[x]==len[p]+1){fa[q]=x;return ;}int y=++cnt;len[y]=len[p]+1;fa[y]=fa[x];for(int i=0;i<26;i++)ch[y][i]=ch[x][i];fa[x]=fa[q]=y;for(;p&&ch[p][c]==x;p=fa[p])ch[p][c]=y;}
}SA,SB;
struct SQM{int ch[N][26],fa[N],last[26];int cnt;void init(){cnt=1;for(int i=0;i<26;i++)last[i]=1;}void insert(int c){int p=last[c],q=++cnt;fa[q]=p;for(int i=0;i<26;i++)for(int x=last[i];x&&!ch[x][c];x=fa[x])ch[x][c]=q;last[c]=q;}
}SQA,SQB;
struct Node{int x,y,len;
};
int vis[N][N];
int bfs(int opt)
{queue<Node> Q;Q.push({1,1,0});vis[1][1]=opt;while(!Q.empty()){int x=Q.front().x,y=Q.front().y,len=Q.front().len;Q.pop();for(int i=0,xx,yy;i<26;i++){xx=(opt<=2 ? SA.ch[x][i] : SQA.ch[x][i]),yy=(opt&1? SB.ch[y][i] : SQB.ch[y][i]);if(xx&&yy){if(vis[xx][yy]==opt)continue;//if(opt==2)cout<<char(i+'a')<<" "<<x<<" "<<y<<":"<<xx<<" "<<yy<<" "<<len+1<<"\n";vis[xx][yy]=opt;Q.push({xx,yy,len+1});}if(xx&&!yy) return len+1;}}return -1;
}
int n,m;
char A[N],B[N];
int ans[5];
void work()
{scanf("%s%s",A+1,B+1);n=strlen(A+1);m=strlen(B+1);SA.init(),SB.init(),SQA.init(),SQB.init();for(int i=1;i<=n;i++)SA.insert(A[i]-'a'),SQA.insert(A[i]-'a');for(int i=1;i<=m;i++)SB.insert(B[i]-'a'),SQB.insert(B[i]-'a');ans[1]=bfs(1);ans[2]=bfs(2);ans[3]=bfs(3),ans[4]=bfs(4);for(int i=1;i<=4;i++)printf("%d\n",ans[i]);
}
int main()
{//freopen("sus.in","r",stdin);freopen("sus.out","w",stdout);work();return 0;
}