我又来水sam了!!!
Description:
给出两个长度均不超过5000的字符串
s1
,
s2
,求这两个串中,都只出现一次的最短公共子串。
Solution:
这题可以 O(N2) 过,用 SA 可以做到 O(Nlog2N) ,但是本蒟蒻不会 SA ,所以无奈写了一发 SAM ,时间复杂度 O(N) 。
具体做法就是先建立两个串的共同的
SAM
,然后对于每个节点记录一个
short
表示从根节点到当前节点的最短路径,然后求出每个节点分别在两个串中出现的次数
g1,g2
,对于一个节点
pot
,如果有
pot.g1==pot.g2==1
,那么就用
pot.short
来更新
ans
Code:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
char str1[5010]="\0";
char str2[5010]="\0";
int len1=0,len2=0;
struct trie
{
int ch[26];
int fa;
int Link;
}tree[10010]={{{0},0,0}};
int tp=0;
int Np=0;
struct sam
{
int ch[26];
int lenth;
int fail;
int g1,g2;
int Short;
}pot[20010]={{{0},0,0,0,0,0}};
int pp=0;
int S[10010]={0};
int rank[20010]={0};
int New[10010]={0};
int dui[10010]={0};
int duip=0;
void Add(int t,int c,int Pre)
{
int cnt=New[Pre];New[t]=++pp;pot[New[t]].lenth=pot[New[Pre]].lenth+1;
for(;cnt!=0 && pot[cnt].ch[c]==0;cnt=pot[cnt].fail)
pot[cnt].ch[c]=New[t];
if(cnt==0) pot[New[t]].fail=1;
else
{
int q=pot[cnt].ch[c];
if(pot[q].lenth==pot[cnt].lenth+1)
pot[New[t]].fail=q;
else
{
pp++;pot[pp]=pot[q];pot[pp].lenth=pot[cnt].lenth+1;
pot[q].fail=pot[New[t]].fail=pp;
for(;cnt!=0 && pot[cnt].ch[c]==q;cnt=pot[cnt].fail)
pot[cnt].ch[c]=pp;
}
}
return;
}
int main()
{
int ans=2e9;
scanf("%s",str1+1);
scanf("%s",str2+1);
len1=strlen(str1+1);
len2=strlen(str2+1);
tp=1;
int cnt=1;
for(int i=1;i<=len1;i++)
{
int c=str1[i]-'a';
if(tree[cnt].ch[c]==0)
{
tree[cnt].ch[c]=++tp;
tree[tp].fa=cnt;
}
cnt=tree[cnt].ch[c];
}
cnt=1;
for(int i=1;i<=len2;i++)
{
int c=str2[i]-'a';
if(tree[cnt].ch[c]==0)
{
tree[cnt].ch[c]=++tp;
tree[tp].fa=cnt;
}
cnt=tree[cnt].ch[c];
}
tree[1].Link=0;
New[0]=++pp;
dui[++duip]=1;
for(int i=1;i<=duip;i++)
{
int u=dui[i];
for(int c=0;c<26;c++)
if(tree[u].ch[c]!=0)
{
int v=tree[u].ch[c];
dui[++duip]=v;
tree[v].Link=++Np;
Add(Np,c,tree[u].Link);
}
}
cnt=1;
for(int i=1;i<=len1;i++)
{
int c=str1[i]-'a';
cnt=pot[cnt].ch[c];
pot[cnt].g1++;
}
cnt=1;
for(int i=1;i<=len2;i++)
{
int c=str2[i]-'a';
cnt=pot[cnt].ch[c];
pot[cnt].g2++;
}
for(int i=1;i<=pp;i++)
{
S[pot[i].lenth]++;
pot[i].Short=2e9;
}
pot[1].Short=0;
for(int i=1;i<=len1 || i<=len2;i++)
S[i]+=S[i-1];
for(int i=1;i<=pp;i++)
rank[S[pot[i].lenth]--]=i;
for(int i=1;i<=pp;i++)
{
int u=rank[i];
for(int c=0;c<26;c++)
if(pot[u].ch[c]!=0)
pot[pot[u].ch[c]].Short=min(pot[pot[u].ch[c]].Short,pot[u].Short+1);
}
for(int i=pp;i>=2;i--)
{
if(pot[rank[i]].g1==1 && pot[rank[i]].g2==1)
ans=min(pot[rank[i]].Short,ans);
pot[pot[rank[i]].fail].g1+=pot[rank[i]].g1;
pot[pot[rank[i]].fail].g2+=pot[rank[i]].g2;
}
if(ans==2e9)
ans=-1;
cout<<ans<<endl;
return 0;
}