蒟蒻写这篇 b l o g blog blog主要是存一下,后缀自动机的详细搭建过程,方便以后复习
具体的某些证明,为什么这么做,正确性劈里啪啦一大堆就不赘述了讲解指路☞
后缀自动机上每一条到 i i i的路径对应一个子串,整个自动机包含了字符串的所有子串
很多时候可以和后缀数组等价使用
e n d p o s endpos endpos:一个子串 i i i在整个字符串中出现的位置 最后一个字符的下标 构成的集合
举个栗子 a b c b c d e a b c abcbcdeabc abcbcdeabc,(从 0 0 0开始标号)
子串 a b c abc abc对应的 e n d p o s endpos endpos为 { 2 , 9 } \{2,9\} {2,9},子串 b c bc bc的 e n d p o s endpos endpos为 { 2 , 4 , 9 } \{2,4,9\} {2,4,9}
后缀自动机的编号对应的就是 e n d p o s endpos endpos完全相同的所有子串
依旧是上面的粒子 a b c b c d e a b c abcbcdeabc abcbcdeabc
子串 b c bc bc的 e n d p o s endpos endpos为 { 2 , 4 , 9 } \{2,4,9\} {2,4,9},子串 c c c的 e n d p o s endpos endpos也为 { 2 , 4 , 9 } \{2,4,9\} {2,4,9}
那么后缀自动机上对应的 i d id id编号既表示 b c bc bc子串,也表示 c c c子串
⚡
l e n [ x ] len[x] len[x]复制点的 l e n len len不等于被复制点的原后缀链接的 l e n + 1 len+1 len+1,而是谁触发的 l e n + 1 len+1 len+1
struct node {
int len; //长度
int fa; //后缀链接
int son[maxc]; //字符集大小
}t[maxn];
模拟从主链的前一个开始跳后缀链接,并对于链接上的没有该字符边的每一个点都连出一条新字符边
while( pre && ! t[pre].son[c] ) t[pre].son[c] = now, pre = t[pre].fa;
跳到根,代表这是首个出现的字符,他只能链接最初的根节点了
if( ! pre ) t[now].fa = 1;
否则,如果路上找到了,满足 l e n len len的关系,直接后缀链接指过去即可
int u = t[pre].son[c];
if( t[u].len == t[pre].len + 1 ) t[now].fa = u;
复制该点,并进行有关该点的所有信息重改
①原点连出的点,新点也要连出
②连入原点的点,变成连入新点
③原点和新点间也需建立联系,新点是原点的后缀链接
else {
int v = ++ tot;
t[v] = t[u];//利用结构体巧妙将原点连出的点进行复制
t[v].len = t[pre].len + 1;//由谁触发 len就是触发点len+1
t[u].fa = t[now].fa = v;//原点与复制点与新建点的关系
while( pre && t[pre].son[c] == u ) t[pre].son[c] = v, pre = t[pre].fa;//暴力复制修改连入原点的点
}
#include
#include
#include
using namespace std;
#define maxn 2000005
vector < int > G[maxn];
struct node {
int fa, len;
int son[30];
}t[maxn];
char s[maxn];
int last = 1, tot = 1;
long long ans;
int siz[maxn];
void insert( int c ) {
int pre = last, now = last = ++ tot;
siz[tot] = 1;
t[now].len = t[pre].len + 1;
while( pre && ! t[pre].son[c] ) t[pre].son[c] = now, pre = t[pre].fa;
if( ! pre ) t[now].fa = 1;
else {
int u = t[pre].son[c];
if( t[u].len == t[pre].len + 1 ) t[now].fa = u;
else {
int v = ++ tot;
t[v] = t[u];
t[v].len = t[pre].len + 1;
t[u].fa = t[now].fa = v;
while( pre && t[pre].son[c] == u ) t[pre].son[c] = v, pre = t[pre].fa;
}
}
}
void dfs( int u ) {
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
dfs( v );
siz[u] += siz[v];
}
if( siz[u] != 1 ) ans = max( ans, 1ll * siz[u] * t[u].len );
}
int main() {
scanf( "%s", s );
int len = strlen( s );
for( int i = 0;i < len;i ++ ) insert( s[i] - 'a' );
for( int i = 2;i <= tot;i ++ ) G[t[i].fa].push_back( i );
dfs( 1 );
printf( "%lld", ans );
return 0;
}
#include
#include
#include
#include
using namespace std;
#define inf 0x7f7f7f7f
#define int long long
#define maxn 600005
struct node {
int len, fa;
int son[30];
}t[maxn];
vector < int > G[maxn];
int last = 1, cnt = 1, n;
char s[maxn];
int a[maxn], f[maxn], tot[maxn], siz[maxn], maxx[maxn], minn[maxn];
void insert( int x, int w ) {
int pre = last, now = last = ++ cnt;
siz[now] = 1, t[now].len = t[pre].len + 1;
maxx[now] = minn[now] = w;
while( pre && ! t[pre].son[x] ) t[pre].son[x] = now, pre = t[pre].fa;
if( ! pre ) t[now].fa = 1;
else {
int u = t[pre].son[x];
if( t[u].len == t[pre].len + 1 ) t[now].fa = u;
else {
int v = ++ cnt;
maxx[v] = -inf, minn[v] = inf;
t[v] = t[u];
t[v].len = t[pre].len + 1;
t[u].fa = t[now].fa = v;
while( pre && t[pre].son[x] == u ) t[pre].son[x] = v, pre = t[pre].fa;
}
}
}
bool check( int u ) {
return maxx[u] != -inf && minn[u] != inf;
}
void dfs( int u ) {
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
dfs( v );
tot[t[u].len] += siz[u] * siz[v];
siz[u] += siz[v];
if( check( u ) )
f[t[u].len] = max( f[t[u].len], max( maxx[u] * maxx[v], minn[u] * minn[v] ) );
maxx[u] = max( maxx[u], maxx[v] );
minn[u] = min( minn[u], minn[v] );
}
}
signed main() {
memset( f, -0x7f, sizeof( f ) );
scanf( "%d %s", &n, s + 1 );
for( int i = 1;i <= n;i ++ )
scanf( "%lld", &a[i] );
for( int i = n;i;i -- ) insert( s[i] - 'a', a[i] );
for( int i = 2;i <= cnt;i ++ ) G[t[i].fa].push_back( i );
dfs( 1 );
for( int i = n - 1;~ i;i -- ) tot[i] += tot[i + 1], f[i] = max( f[i], f[i + 1] );
for( int i = 0;i < n;i ++ )
printf( "%lld %lld\n", tot[i], ( tot[i] ? f[i] : 0 ) );
return 0;
}
#include
#include
#include
using namespace std;
#define maxn 5000
char a[maxn], b[maxn];
struct SAM {
struct node {
int len, fa;
int son[30];
}t[maxn];
int last, cnt;
SAM() {
last = cnt = 1;
}
void insert( int x ) {
int pre = last, now = last = ++ cnt;
t[now].len = t[pre].len + 1;
while( pre && ! t[pre].son[x] ) t[pre].son[x] = now, pre = t[pre].fa;
if( ! pre ) t[now].fa = 1;
else {
int u = t[pre].son[x];
if( t[u].len == t[pre].len + 1 ) t[now].fa = u;
else {
int v = ++ cnt;
t[v] = t[u];
t[v].len = t[pre].len + 1;
t[u].fa = t[now].fa = v;
while( pre && t[pre].son[x] == u ) t[pre].son[x] = v, pre = t[pre].fa;
}
}
}
}SamA, SamB;
struct SEQ {
int nxt[maxn][30], last[30];
SEQ() {
memset( nxt, 0, sizeof( nxt ) );
memset( last, 0, sizeof( last ) );
}
void build( int n, char *s ) {
for( int i = n;~ i;i -- ) {
for( int j = 0;j < 26;j ++ )
if( last[j] ) nxt[i + 1][j] = last[j];
if( i ) last[s[i] - 'a'] = i + 1;
}
}
}SeqA, SeqB;
struct node {
int x, y, dep;
node(){}
node( int X, int Y, int Dep ) {
x = X, y = Y, dep = Dep;
}
};
queue < node > q;
bool vis[maxn][maxn];
void init() {
memset( vis, 0, sizeof( vis ) );
while( ! q.empty() ) q.pop();
vis[1][1] = 1, q.push( node( 1, 1, 0 ) );
}
int bfs1() {
init();
while( ! q.empty() ) {
node now = q.front(); q.pop();
for( int i = 0;i < 26;i ++ ) {
int sonA = SamA.t[now.x].son[i];
int sonB = SamB.t[now.y].son[i];
if( vis[sonA][sonB] ) continue;
else if( sonA && ! sonB ) return now.dep + 1;
else if( sonA && sonB ) {
vis[sonA][sonB] = 1;
q.push( node( sonA, sonB, now.dep + 1 ) );
}
}
}
return -1;
}
int bfs2() {
init();
while( ! q.empty() ) {
node now = q.front(); q.pop();
for( int i = 0;i < 26;i ++ ) {
int sonA = SamA.t[now.x].son[i];
int sonB = SeqB.nxt[now.y][i];
if( vis[sonA][sonB] ) continue;
else if( sonA && ! sonB ) return now.dep + 1;
else if( sonA && sonB ) {
vis[sonA][sonB] = 1;
q.push( node( sonA, sonB, now.dep + 1 ) );
}
}
}
return -1;
}
int bfs3() {
init();
while( ! q.empty() ) {
node now = q.front(); q.pop();
for( int i = 0;i < 26;i ++ ) {
int sonA = SeqA.nxt[now.x][i];
int sonB = SamB.t[now.y].son[i];
if( vis[sonA][sonB] ) continue;
else if( sonA && ! sonB ) return now.dep + 1;
else if( sonA && sonB ) {
vis[sonA][sonB] = 1;
q.push( node( sonA, sonB, now.dep + 1 ) );
}
}
}
return -1;
}
int bfs4() {
init();
while( ! q.empty() ) {
node now = q.front(); q.pop();
for( int i = 0;i < 26;i ++ ) {
int sonA = SeqA.nxt[now.x][i];
int sonB = SeqB.nxt[now.y][i];
if( vis[sonA][sonB] ) continue;
else if( sonA && ! sonB ) return now.dep + 1;
else if( sonA && sonB ) {
vis[sonA][sonB] = 1;
q.push( node( sonA, sonB, now.dep + 1 ) );
}
}
}
return -1;
}
int main() {
scanf( "%s %s", a + 1, b + 1 );
int lena = strlen( a + 1 ), lenb = strlen( b + 1 );
for( int i = 1;i <= lena;i ++ )
SamA.insert( a[i] - 'a' );
for( int i = 1;i <= lenb;i ++ )
SamB.insert( b[i] - 'a' );
SeqA.build( lena, a );
SeqB.build( lenb, b );
printf( "%d\n%d\n%d\n%d\n", bfs1(), bfs2(), bfs3(), bfs4() );
return 0;
}
这题运用的思想主要是广义后缀自动机,即将多个字符串建在一个后缀自动机上
其实并没有什么新颖之处,只需在扩展的时候带一个这个字符属于哪个字符串的编号即可
假设已经建好了自动机,接下来考虑两个长度为 k k k的子串之间如何一一对应修改
这个时候如果将其放到 p a r e n t t r e e parent\ tree parent tree上考虑的话,就简单了
其实可以猜想一下,刚开始我就想到了虚树的性质,即相邻两两配对
不难证明,的确应该相邻两个不同属类的子串配对
前缀 i , j i,j i,j的最长公共后缀 = p a r e n t t r e e =parent\ tree =parent tree上前缀 i , j i,j i,j分别指向的点 u , v u,v u,v的 l c a lca lca反映在后缀自动机上的节点代表的最长子串
也就是最后变成深搜一棵树的模样,记得特判可能 l c a lca lca代表的最长子串长度 ≥ k \ge k ≥k
此时是不需要代价的
#include
#include
#include
#include
using namespace std;
#define maxn 600005
struct node {
int len, fa;
int son[30];
}t[maxn];
vector < int > G[maxn];
int n, k, last = 1, cnt = 1;
long long ans;
char a[maxn], b[maxn];
int type[maxn];
int tot[maxn][3];
void insert( int x, int s, int pos ) {
int pre = last, now = last = ++ cnt;
t[now].len = t[pre].len + 1;
while( pre && ! t[pre].son[x] ) t[pre].son[x] = now, pre = t[pre].fa;
if( ! pre ) t[now].fa = 1;
else {
int u = t[pre].son[x];
if( t[u].len == t[pre].len + 1 ) t[now].fa = u;
else {
int v = ++ cnt;
t[v] = t[u];
t[v].len = t[pre].len + 1;
t[u].fa = t[now].fa = v;
while( pre && t[pre].son[x] == u ) t[pre].son[x] = v, pre = t[pre].fa;
}
}
if( pos >= k ) type[now] = s;
}
void dfs( int u ) {
tot[u][type[u]] ++;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
dfs( v );
tot[u][1] += tot[v][1];
tot[u][2] += tot[v][2];
}
if( tot[u][1] >= tot[u][2] ) {
int x = max( 0, k - t[u].len );
ans += 1ll * x * tot[u][2];
tot[u][1] -= tot[u][2];
tot[u][2] = 0;
}
else {
int x = max( 0, k - t[u].len );
ans += 1ll * x * tot[u][1];
tot[u][2] -= tot[u][1];
tot[u][1] = 0;
}
}
int main() {
scanf( "%d %d %s %s", &n, &k, a + 1, b + 1 );
reverse( a + 1, a + n + 1 );
reverse( b + 1, b + n + 1 );
for( int i = 1;i <= n;i ++ ) insert( a[i] - 'a', 1, i );
for( int i = 1;i <= n;i ++ ) insert( b[i] - 'a', 2, i );
for( int i = 2;i <= cnt;i ++ ) G[t[i].fa].push_back( i );
dfs( 1 );
printf( "%lld", ans );
return 0;
}