BZOJ 3277 串 & BZOJ 3473 字符串 (廣義後綴自動機、時間複雜度分析)

標籤那麼長是由於作法太多了。。。php

題目連接: (bzoj 3277) https://www.lydsy.com/JudgeOnline/problem.php?id=3277函數

(bzoj 3473) https://www.lydsy.com/JudgeOnline/problem.php?id=3473url

題解:spa

先講三個作法公共部分: 建出廣義SAM,而後對於每一個點求出它在多少字符串中出現過。.net

作法一

把每一個字符串在廣義SAM上暴力跑。每跑到一個點就暴力沿着fail樹往上跳,標記跳過的點,直到跳到已標記的點爲止(每一個串要換用不一樣的標記)。code

時間複雜度$O(L\sqrt L)$ ($n$爲串的個數,$L$爲總長度)ci

寫一下時間複雜度分析: (我本身想的,頗有多是錯的,有錯懇請大佬指出!!感謝)字符串

假設某個字符串長度爲$x$, 則最壞狀況下它一直在往深處走,而且每一步都沒有碰到已經跳過的點,這種狀況下其走的步數是$\sum^{x}_{i=1}i=O(x^2)$.get

可是它還要受到另外一個限制,就是走的步數不超過SAM總大小$O(L)$. 所以其對時間複雜度貢獻爲$O(\min(x^2),L)$.string

計算最壞狀況下的時間複雜度,也就是已知$\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;
}
相關文章
相關標籤/搜索