A string is finite sequence of characters over a non-empty finite set Σ.
In this problem, Σ is the set of lowercase letters.
Substring, also called factor, is a consecutive sequence of characters occurrences at least once in a string.
Now your task is a bit harder, for some given strings, find the length of the longest common substring of them.
Here common substring means a substring of two or more strings.
Input
The input contains at most 10 lines, each line consists of no more than 100000 lowercase letters, representing a string.
Output
The length of the longest common substring. If such string doesn’t exist, print “0” instead.
Example
Input: alsdfkjfjkdsal fdjskalajfkdsla aaaajfaaaa Output: 2
Notice: new testcases added
题意: 给出k个串 求这k个串的最长公共子串
这是一道经典的后缀自动机模板题。
这里利用了后缀自动机的一些性质
1。 每个状态存储了一连串后缀的集合。
2。 整个后缀自动机所有状态可以表示整个串的所有子串
3。通过前驱节点连接的一条链上的所有点,可以表示一个子串的所有后缀。
那么,对于这个题,我们可以对一个串建立后缀自动机,然后让其他串去匹配。
每匹配一个串,我们就可以更新这个串的所有状态对应最长公共子串的长度。
然后取全局最大即可。
要注意的是,我们不可能每次都匹配到后缀自动机的所有状态,这样就会导致某些状态被遗漏,所以,我们需要通过儿子节点去更新父亲节点。
这里可以通过dfs,拓扑序,或者基数排序(很对题解都是用这个方法,我完全想不到可以直接这么排序,他们太强了)更新。
中间有一些细节,就不一一讲述了,我在代码中将我觉得要注意的地方标注了一下。
总的来说 还是一道简单模板题。。
#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define x first
#define y second
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=a-1;i>=b;--i)
#define fuck(x) cout<<'['<<#x<<' '<<(x)<<']'
#define FIN freopen("in.txt","r",stdin);
using namespace std;
#define N 2000050
typedef long long LL;
char s[N];
int ch[N][27],len[N],link[N],rd[N],last,tot,rt,n;
int F[N],ans[N];
int cnt[N];
char str[N];
void add(int pos) {
int x = s[pos] - 'a' + 1 , p = last , np = ++tot;
last = np;
len[np] = len[p] + 1;
F[np] = 1LL;
while (p && !ch[p][x]) ch[p][x] = np , p = link[p];
if (!p)
link[np] = rt;
else {
int q = ch[p][x];
if (len[q] == len[p] + 1)
link[np] = q;
else {
int nq = ++tot;
len[nq] = len[p] + 1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
link[nq] = link[q];
link[q] = link[np] = nq;
while (p && ch[p][x] == q) ch[p][x] = nq , p = link[p];
}
}
return ;
}
void getsize(){
for (int i=1;i<=tot;i++) rd[ link[i] ]++;
queue<int> q;
for (int i=1;i<=tot;i++) if (!rd[i]) q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop();
F[ link[u] ] += F[u];
cnt[link[u]] = max(cnt[link[u]],min(cnt[u],len[link[u]]));
if (--rd[ link[u] ] == 0) q.push(link[u]);
}
}
void update(){
int lens = strlen(str);
int now = rt;
for(int i=1;i<=tot;i++){
cnt[i]= 0;
}
// tmp表示当前状态最大长度(重点!)
int tmp=0;
for(int i=0;i<lens;i++){
int x = str[i] - 'a' + 1;
while(!ch[now][x] && now){
now = link[now];
}
tmp = min(tmp,len[now]);
if(ch[now][x]) tmp++;
if(!now){
now = rt;
}else{
cnt[ch[now][x]]=max(tmp,cnt[ch[now][x]]);
now = ch[now][x];
}
}
// 拓扑序更新父亲节点状态
getsize();
// 局部取最小
for(int i=1;i<=tot;i++){
ans[i]=min(ans[i],cnt[i]);
}
}
void init(){
rt = last = ++tot;
}
int main() {
init();
scanf("%s",s+1); n = strlen(s+1);
for (int i=1;i<=n;i++) add(i);
for(int i=1;i<=tot;i++){
ans[i]=len[i];
}
while(~scanf("%s",str)){
update();
}
int mins=-1;
// 全局取最大
for(int i=1;i<=tot;i++){
mins=max(mins,ans[i]);
}
cout<<mins<<endl;
return 0;
}