算法学习:后缀自动机

【前置知识】

AC自动机(没有什么关联,但是看懂了会对后缀自动机有不同的理解)

 

【解决问题】

各种子串的问题

 

 

【算法学习】

学习后缀自动机的过程中,看到了许多相关性质和证明,但是奈何才疏学浅(lan)

暂时先放着,到有空再更

 

【算法分析】

后缀自动机和AC自动机和回文自动机的不同点在于

后缀自动机是个DAG,而AC自动机和回文自动机是

 

 

首先理解   endpos 数组,每个子串都有一个endpos数组,表示他在字符串中出现的位置的结束位置

而endpos数组相同的子串,就被称为endpos类

而显然易见的,endpos类中最长的子串,能够在子类中找到他的所有子串

而后缀自动机中的节点,就是endpos类,所代表的字符串,也可以表示为这个endpos类中最长的子串

 

节点之间的边,是互相之间的字母

通过字母走到节点,能够得到关于这个节点子串的后缀

即此节点所代表的整个 endpos 类

 

right [ x ] /zhi [ x ] 数组代表的是,这个节点所代表的子串出现过的次数

maxlen [ x ] / len 数组表示的是,这个节点所代表的endpos类中出现的最长子串长度

 

 

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
using namespace std;
const int MAXN = 2*1e6 + 10;
const int CHAR = 27;
struct note
{
    int ch[CHAR];
    int fa;
    ll len;
}dian[MAXN];
int zhi[MAXN];
int cnt=1, now=1;
char s[MAXN];
void insert(int c)
{
    int p = now, np = now = ++cnt; zhi[cnt] = 1;
    dian[np].len = dian[p].len + 1;
    for (;p && !dian[p].ch[c]; p = dian[p].fa) 
        dian[p].ch[c] = np;
    if (!p) dian[np].fa = 1;
    else
    {
        int q = dian[p].ch[c];
        if (dian[q].len == dian[p].len + 1) dian[np].fa = q;
        else
        {
            int nq = ++cnt;
            dian[nq] = dian[q];
            dian[nq].len = dian[p].len + 1;
            dian[q].fa = dian[np].fa = nq;
            for (; p && dian[p].ch[c] == q; p = dian[p].fa)
                dian[p].ch[c] = nq;
        }
    }
}
ll ans = 0;
int cd;
int st[MAXN],top;
struct NOTE
{
    int to;
    int nt;
}edge[MAXN];
void add(int x, int y)
{
    top++; edge[top].nt = st[x]; edge[top].to = y; st[x] = top;
}
void dfs(int x)
{
    for (int i = st[x]; i != -1; i = edge[i].nt)
    {
        dfs(edge[i].to);
        zhi[x] += zhi[edge[i].to];
    }
    if (zhi[x] != 1) ans = max(ans, zhi[x] * dian[x].len);
}
int main()
{
    memset(st, -1, sizeof(st));
    scanf("%s", s); cd = strlen(s);
    //printf("**\n");
    for (int i = 0; i < cd; i++) insert(s[i] - 'a'+1);
    //printf("**\n");
    for (int i = 2; i <= cnt; i++)
        add(dian[i].fa, i);
    //printf("**\n");
    dfs(1);
    printf("%lld\n", ans);

    return 0;
}

 

 

 


【模板题】

【HDU 6194】

求字符串中出现 k 次的子串个数

【思路分析】

right [ x ] 数组中保存的就是这个 endpos类 所包含的所有子串出现的次数,而当这个次数等于 k 时,就说明有出现 k 次的子串

之所以需要用 maxlen [ x ] 减去 maxlen [ fa [ x ] ] 是因为。。。。。好吧,我还不是很熟 fa [ x ] 所代表的含义

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<queue>
 4 #include<algorithm>
 5 #define ll long long
 6 using namespace std;
 7 const int MAXN = 200000+ 10;
 8 const int CHAR = 27;
 9 struct note
10 {
11     int ch[CHAR];
12     int fa;
13     int len;
14 }dian[MAXN];
15 int zhi[MAXN];
16 int cnt = 1, now = 1;
17 char s[MAXN];
18 void insert(int c)
19 {
20     int p = now, np = now = ++cnt; 
21     zhi[cnt] = 1;
22     dian[np].len = dian[p].len + 1;
23     for (; p && !dian[p].ch[c]; p = dian[p].fa)
24         dian[p].ch[c] = np;
25     if (!p) dian[np].fa = 1;
26     else
27     {
28         int q = dian[p].ch[c];
29         if (dian[q].len == dian[p].len + 1) dian[np].fa = q;
30         else
31         {
32             int nq = ++cnt;
33             zhi[cnt] = 0;
34             dian[nq] = dian[q];
35             dian[nq].len = dian[p].len + 1;
36             dian[q].fa = dian[np].fa = nq;
37             for (; p && dian[p].ch[c] == q; p = dian[p].fa)
38                 dian[p].ch[c] = nq;
39         }
40     }
41 }
42 int cd;
43 void build()
44 {
45     static int c[MAXN] = { 0 };
46     static int ss[MAXN] = { 0 };
47     memset(c, 0, sizeof(c));
48     for (int i = 1; i <= cnt; i++)
49     {
50         c[dian[i].len]++;
51     }
52     for (int i = 1; i <= cd; i++)
53         c[i] += c[i - 1];
54     for (int i = 1; i<=cnt; i++)
55     {
56         ss[c[dian[i].len]--] = i;
57     }
58     for (int i =cnt; i>=2; i--)
59     {
60         zhi[dian[ss[i]].fa] += zhi[ss[i]];
61     }
62     return;
63 }
64 void init()
65 {
66     for (int i = 0; i <= cnt; i++)
67     {
68         for (int j = 0; j < CHAR; j++)
69             dian[i].ch[j] = 0;
70         dian[i].fa = 0;
71         dian[i].len = 0;
72         zhi[i] = 0;
73     }cnt = now = 1;
74     return;
75 }
76 int main()
77 {
78     int T;
79     scanf("%d", &T);
80     while(T--)
81     {
82         int k;
83         scanf("%d", &k);
84         scanf("%s", s); cd = strlen(s);
85         for (int i = 0; i < cd; i++) insert(s[i] - 'a' + 1);
86         build();
87         ll ans = 0;
88         //printf("***\n");
89         //    for (int i = 1; i <= cnt; i++)
90         //    printf("%d\n", zhi[i]);
91         //printf("***\n");
92         for (int i = 2; i <= cnt; i++)
93             if (zhi[i] == k)
94                 ans += dian[i].len - dian[dian[i].fa].len;
95         printf("%lld\n", ans);
96         init();
97     }
98     return 0;
99 }

 

posted @ 2019-09-11 20:48  rentu  阅读(228)  评论(0编辑  收藏  举报