BZOJ 3277 串 BZOJ 3473 字符串 (广义后缀自动机、时间复杂度分析)
標簽那么長是因為做法太多了。。。
題目鏈接: (bzoj 3277) https://www.lydsy.com/JudgeOnline/problem.php?id=3277
(bzoj 3473) https://www.lydsy.com/JudgeOnline/problem.php?id=3473
題解:
先講三個做法公共部分: 建出廣義SAM,然后對于每個點求出它在多少字符串中出現過。
做法一
把每個字符串在廣義SAM上暴力跑。每跑到一個點就暴力沿著fail樹往上跳,標記跳過的點,直到跳到已標記的點為止(每個串要換用不同的標記)。
時間復雜度\(O(L\sqrt L)\) (\(n\)為串的個數,\(L\)為總長度)
寫一下時間復雜度分析: (我自己想的,很有可能是錯的,有錯懇請大佬指出!!感謝)
假設某個字符串長度為\(x\), 則最壞情況下它一直在往深處走,并且每一步都沒有碰到已經跳過的點,這種情況下其走的步數是\(\sum^{x}_{i=1}i=O(x^2)\).
但是它還要受到另一個限制,就是走的步數不超過SAM總大小\(O(L)\). 因此其對時間復雜度貢獻為\(O(\min(x^2),L)\).
計算最壞情況下的時間復雜度,也就是已知\(\sum^{m}_{i=1} x_i=L\), 求\(\sum^{m}_{i=1} \min(x_i^2,L)\)的最大值。顯然當\(x_i>\sqrt L\)時是沒有任何意義的(白白浪費代價,價值不增加),所以就是已知\(\sum^{m}_{i=1} x_i=L\)且對于任意\(i\)有\(i\le \sqrt L\), 求\(\sum^{m}_{i=1} x_i^2\)的最大值。由函數的凹凸性知顯然(或者也可以用偏導數解釋,如果你愿意的話。。。)所有串長均為\(\sqrt L\)時目標函數最大,為\(O(L\sqrt L)\).
做法二
類似于BZOJ2754/BZOJ2780, 就是個數顏色問題,每個點開個set記錄經過這個點的所有串,然后沿著Parent樹自下而上啟發式合并。時間復雜度\(O(n\log^2n)\).
線段樹合并貌似可以做到\(O(n\log n)\)?
做法三
依然是數顏色,可以使用DFS序+主席樹等各種神奇做法解決。時間復雜度\(O(n\log^2n)\) (?)
代碼
做法一
#include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<vector> #define llong long long using namespace std;const int N = 4e5; const int S = 26; struct SAM {int id[N+3];int fa[N+3];int son[N+3][S+3];int len[N+3];int sz[N+3];int buc[N+3];int oid[N+3];int cnt[N+3];int cid[N+3];int mx[N+3];int siz,rtn,lstpos;void init() {id[0] = siz = rtn = lstpos = 1;}int insertstr(char ch){int p = lstpos,np; siz++; np = lstpos = siz; len[np] = len[p]+1; sz[np] = 1;for(; p && son[p][ch]==0; p=fa[p]) {son[p][ch] = np;}if(p==0) fa[np] = rtn;else{int q = son[p][ch];if(len[q]==len[p]+1) {fa[np] = q;}else{siz++; int nq = siz; len[nq] = len[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));fa[nq] = fa[q]; fa[np] = fa[q] = nq;for(; p && son[p][ch]==q; p=fa[p]) {son[p][ch] = nq;}}}return np;}void getsort(){for(int i=1; i<=siz; i++) buc[len[i]]++;for(int i=1; i<=siz; i++) buc[i] += buc[i-1];for(int i=siz; i>=1; i--) oid[buc[len[i]]--] = i;} } sam; vector<char> str[N+3]; char a[N+3]; int n,m;int main() {scanf("%d%d",&n,&m);sam.init();for(int i=1; i<=n; i++){scanf("%s",a+1);sam.lstpos = 1;int lena = strlen(a+1);for(int j=1; j<=lena; j++) {sam.insertstr(a[j]-96); str[i].push_back(a[j]);}} // for(int i=1; i<=sam.siz; i++) for(int j=1; j<=S; j++) if(sam.son[i][j]) printf("trans%d %d %d\n",i,j,sam.son[i][j]); // for(int i=1; i<=sam.siz; i++) printf("i%d len%d fa%d\n",i,sam.len[i],sam.fa[i]);sam.getsort();for(int i=1; i<=n; i++){int pos = sam.rtn;for(int j=0; j<str[i].size(); j++){pos = sam.son[pos][str[i][j]-96];int tmp = pos;for(; tmp && sam.cid[tmp]!=i; tmp=sam.fa[tmp]) {sam.cnt[tmp]++; sam.cid[tmp] = i;}}}sam.cnt[1] = 0;for(int i=1; i<=sam.siz; i++){int u = sam.oid[i];sam.mx[u] = sam.mx[sam.fa[u]]+(sam.cnt[u]>=m ? sam.len[u]-sam.len[sam.fa[u]] : 0); // printf("%d mx%d\n",u,sam.mx[u]);}for(int i=1; i<=n; i++){int pos = sam.rtn; llong ans = 0ll;for(int j=0; j<str[i].size(); j++){pos = sam.son[pos][str[i][j]-96];ans += (llong)sam.mx[pos]; // putchar(str[i][j]); printf("pos%d\n",pos); puts("");}printf("%lld ",ans);}return 0; }總結
以上是生活随笔為你收集整理的BZOJ 3277 串 BZOJ 3473 字符串 (广义后缀自动机、时间复杂度分析)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BZOJ 2806 Luogu P402
- 下一篇: BZOJ 3277 串 BZOJ 34