后缀自动机

一、SAM的性质:

SAM是个状态机。一个起点,若干终点。原串的所有子串和从SAM起点开始的所有路径一一对应,不重不漏。所以终点就是包含后缀的点。
每个点包含若干子串,每个子串都一一对应一条从起点到该点的路径。且这些子串一定是里面最长子串的连续后缀。
SAM问题中经常考虑两种边:
(1) 普通边,类似于Trie。表示在某个状态所表示的所有子串的后面添加一个字符。
(2) Link、Father。表示将某个状态所表示的最短子串的首字母删除。这类边构成一棵树。
二、SAM的构造思路

endpos(s):子串s所有出现的位置(尾字母下标)集合。SAM中的每个状态都一一对应一个endpos的等价类。
endpos的性质:
(1) 令 s1,s2 为 S 的两个子串 ,不妨设 |s1|≤|s2| (我们用 |s| 表示 s 的长度 ,此处等价于 s1 不长于 s2 )。则 s1 是 s2 的后缀当且仅当 endpos(s1)⊇endpos(s2) ,s1 不是 s2 的后缀当且仅当 endpos(s1)∩endpos(s2)=∅ 。
(2) 两个不同子串的endpos,要么有包含关系,要么没有交集。
(3) 两个子串的endpos相同,那么短串为长串的后缀。
(4) 对于一个状态 st ,以及任意的 longest(st) 的后缀 s ,如果 s 的长度满足:|shortest(st)|≤|s|≤|longsest(st)| ,那么 s∈substrings(st) 。

 

 

 

SAM的应用

1可以将其看作一字符串的hash表,用于查询

2求该字符串的不同子串的数量//每一个点包括的字符串集合

 

3求某一子串在该改串的出现次数->划分endpos,递归的找

 

 

 

给定一个长度为 n 的只包含小写字母的字符串 S。

对于所有 S 的出现次数不为 1 的子串,设其 value 值为该子串出现的次数 × 该子串的长度。

请计算,value 的最大值是多少。

输入格式

共一行,包含一个由 n个小写字母构成的字符串。

输出格式

共一行,输出一个整数,表示答案。

数据范围

1n1e6
保证至少存在一个子串出现次数大于 11。

输入样例:

aabab

输出样例:

4
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 2000010;

int tot = 1, last = 1;
struct Node
{
    int len, fa;
    int ch[26];
}node[N];
char str[N];
LL f[N], ans;
int h[N], e[N], ne[N], idx;

void extend(int c)
{
    int p = last, np = last = ++ tot;
    f[tot] = 1;
    node[np].len = node[p].len + 1;
    for (; p && !node[p].ch[c]; p = node[p].fa) node[p].ch[c] = np;
    if (!p) node[np].fa = 1;
    else
    {
        int q = node[p].ch[c];
        if (node[q].len == node[p].len + 1) node[np].fa = q;
        else
        {
            int nq = ++ tot;
            node[nq] = node[q], node[nq].len = node[p].len + 1;
            node[q].fa = node[np].fa = nq;
            for (; p && node[p].ch[c] == q; p = node[p].fa) node[p].ch[c] = nq;
        }
    }
}

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u)
{
    for (int i = h[u]; ~i; i = ne[i])
    {
        dfs(e[i]);
        f[u] += f[e[i]];
    }
    if (f[u] > 1) ans = max(ans, f[u] * node[u].len);
}

int main()
{
    scanf("%s", str);
    for (int i = 0; str[i]; i ++ ) extend(str[i] - 'a');
    memset(h, -1, sizeof h);
    for (int i = 2; i <= tot; i ++ ) add(node[i].fa, i);
    dfs(1);
    printf("%lld\n", ans);

    return 0;
}

 

 

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/585844/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

posted @ 2020-11-16 13:59  /Ranger  阅读(76)  评论(0编辑  收藏  举报