【前置知识】
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 }