给定两个字符串A,B(5000),找到最短的唯一公共子串,无解输出-1.
唯一公共子串是指在A和B中均只出现了一次。
对A#B
建立后缀自动机,维护endpos集的三个属性:出现次数cnt、第一次出现位置firstpos、最后一次出现位置lastpos.
遍历所有节点,如果cnt=2且 , ,那么这个节点对应的最短字符串 是一个可行的答案。
注意一点,出现次数为2的字符串,一定不会是含有#
号的。
本来不想写题解的,但是这道题几乎把能犯的错误全犯了,特此总结一下:
- 交错题
- 字符集映射错,要添加 号,但是忘了把字符集扩展到128.
- 01下标起始搞乱,设置pos的时候按1下标,算的时候又按0下标,就错了.
- lastpos数组没有开两倍空间.
状态好差啊,睡会再写。
string suffix structrues一共56道题(截至2019-11-13),现在已经做了15道,加油。
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 10016, MOD = 1000000007;
int sz, lst; //后缀自动机大小,上一次插入的节点
int ch[M<<1][128], len[M<<1], link[M<<1], cnt[M<<1];
int firstpos[M<<1], lastpos[M<<1];
void extend(const char *s)
{
for(int i=0; s[i]; ++i)
{
int c = s[i];
int cur = ++sz;
len[cur] = len[lst] + 1;
int p = lst;
while(!ch[p][c])
{
ch[p][c] = cur;
p = link[p];
}
if(ch[p][c] != cur)
{
int q = ch[p][c];
if(len[p] + 1 == len[q]) link[cur] = q;
else
{
int clone = ++sz;
memcpy(ch[clone], ch[q], sizeof(ch[q]));
link[clone] = link[q];
firstpos[clone] = firstpos[q];
lastpos[clone] = lastpos[q];
len[clone] = len[p]+1;
while(ch[p][c]==q)
{
ch[p][c] = clone;
p = link[p];
}
link[q] = link[cur] = clone;
}
}
lst = cur;
++cnt[cur];
firstpos[cur] = lastpos[cur] = i;
}
vector<int> nodes;
for(int i=1; i<=sz; ++i)
nodes.push_back(i);
sort(nodes.begin(), nodes.end(), [&](int a, int b){
return len[a]>len[b];
});
for(auto u:nodes)
{
firstpos[link[u]] = min(firstpos[link[u]], firstpos[u]);
lastpos[link[u]] = max(lastpos[link[u]], lastpos[u]);
cnt[link[u]] += cnt[u];
}
}
char tex[M];
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
scanf("%s", tex); int n = strlen(tex);
tex[n] = '#';
scanf("%s", tex+n+1);
extend(tex);
int ans = -1;
for(int i=1; i<=sz; ++i)
{
// printf("i=%d cnt=%d first=%d last=%d\n",i,cnt[i],firstpos[i],lastpos[i] );
// printf("len=%d milen=%d\n",len[i],len[link[i]]+1 );
//出现次数为2的,一定不可能是含#的串.
if(cnt[i]==2 && firstpos[i]<n && lastpos[i]>n)
{
if(ans==-1 || len[link[i]]+1<ans)
ans = len[link[i]]+1;
}
}
printf("%d\n",ans );
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}