算法学习:后缀自动机
【前置知識】
AC自動機(沒有什么關(guān)聯(lián),但是看懂了會對后綴自動機有不同的理解)
?
【解決問題】
各種子串的問題
?
?
【算法學(xué)習(xí)】
學(xué)習(xí)后綴自動機的過程中,看到了許多相關(guān)性質(zhì)和證明,但是奈何才疏學(xué)淺(lan)
暫時先放著,到有空再更
?
【算法分析】
后綴自動機和AC自動機和回文自動機的不同點在于
后綴自動機是個DAG,而AC自動機和回文自動機是樹
?
?
首先理解? ?endpos 數(shù)組,每個子串都有一個endpos數(shù)組,表示他在字符串中出現(xiàn)的位置的結(jié)束位置
而endpos數(shù)組相同的子串,就被稱為endpos類
而顯然易見的,endpos類中最長的子串,能夠在子類中找到他的所有子串
而后綴自動機中的節(jié)點,就是endpos類,所代表的字符串,也可以表示為這個endpos類中最長的子串
?
節(jié)點之間的邊,是互相之間的字母
通過字母走到節(jié)點,能夠得到關(guān)于這個節(jié)點子串的后綴
即此節(jié)點所代表的整個 endpos 類
?
right [ x ] /zhi [ x ] 數(shù)組代表的是,這個節(jié)點所代表的子串出現(xiàn)過的次數(shù)
maxlen [ x ] / len 數(shù)組表示的是,這個節(jié)點所代表的endpos類中出現(xiàn)的最長子串長度
?
?
#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】
求字符串中出現(xiàn) k 次的子串個數(shù)
【思路分析】
right [ x ] 數(shù)組中保存的就是這個 endpos類 所包含的所有子串出現(xiàn)的次數(shù),而當(dāng)這個次數(shù)等于 k 時,就說明有出現(xiàn) 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 }?
轉(zhuǎn)載于:https://www.cnblogs.com/rentu/p/11508652.html
總結(jié)
以上是生活随笔為你收集整理的算法学习:后缀自动机的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新设备关联Gitlab
- 下一篇: 关于js私钥加密公钥解密的问题