后缀自动机
对于其中一个串建出SAM,把别的串S放在上面跑。对于每一个经过的节点记录串S能贡献给这个节点的max,再对每一个跑过的串在这个节点上取min,最终节点里min贡献的max就是答案。显然一个串能贡献一个节点,就能贡献这个节点的所有祖先,即fail树上到祖先的链都要更新。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 200005
#define A 28
#define cmax(u,v) ((u)<(v)?(u)=(v):0)
#define cmin(u,v) ((u)>(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
int sum[N];
char s[N];
struct SAM
{
SAM *next[A], *fail;
int len, mi, mx;
}mem[N], *tot, *null, *root, *last, *q[N];
SAM *newSAM(int len)
{
SAM *p = ++tot;
*p = *null;
p->len = p->mi = len;
p->mx = 0;
return p;
}
void init()
{
null = tot = mem;
for(int i = 0; i < A; i++) null->next[i] = null;
null->fail = null;
null->len = null->mi = null->mx = 0;
root = last = newSAM(0);
}
void extend(int v)
{
SAM *p = last, *np = newSAM(p->len + 1); last = np;
for(; p->next[v] == null && p != null; p = p->fail) p->next[v] = np;
if(p==null) np->fail = root;
else
{
SAM *q = p->next[v];
if(q->len == p->len+1) np->fail = q;
else
{
SAM *nq = newSAM(p->len+1);
memcpy(nq->next, q->next, sizeof(nq->next));
nq->fail = q->fail;
q->fail = np->fail = nq;
for(; p->next[v] == q && p != null; p = p->fail) p->next[v] = nq;
}
}
}
void main()
{
init(); scanf("%s",s);
for(int i = 0, ii = strlen(s); i < ii; i++) extend(s[i] - 'a');
int totmem = tot - mem;
for(SAM *p = tot; p != mem; p--) sum[p->len]++;
for(int i = 1, ii = strlen(s); i <= ii; i++) sum[i] += sum[i-1];
for(SAM *p = tot; p != mem; p--) q[sum[p->len]--] = p;
for(; scanf("%s",s) != EOF; )
{
SAM *p = root; int len = 0;
for(int i = 0; s[i]; i++)
{
int v = s[i] - 'a';
if(p->next[v] != null)
{
len++;
p = p->next[v];
cmax(p->mx, len);
}
else
{
for(; p->next[v] == null && p != null; p = p->fail);
if(p->next[v] != null)
{
len = p->len+1;
p = p->next[v];
cmax(p->mx, len);
}
else p = root;
}
}
for(int i = totmem; i != 1; i--)
{
SAM *p = q[i];
cmin(p->mi, p->mx);
cmax(p->fail->mx, min(p->fail->len,p->mx));
p->mx = 0;
}
}
int ans = 0;
for(int i = totmem; i; i--) cmax(ans, mem[i].mi);
printf("%d\n",ans);
}
}
int main()
{
runzhe2000::main();
}