1. 程式人生 > >SPOJ8093 BZOJ2780 Sevenk Love Oimaster SAM

SPOJ8093 BZOJ2780 Sevenk Love Oimaster SAM

題目連結

題意:
給你 n n 個字串,有 q q 次詢問,每次給你一個字串,求這個字串在 n

n 個串中的多少個串中出現過。總串長都是1e5量級的,字符集是小寫字母。

題解:
雖然據說AC自動機可能也可以做,但是顯然沒有用字尾自動機方便。
我們對於這 n n 個串建出SAM,串與串之間加一個分隔符隔開。然後我們要對於這個串的所有子串的出現次數都+1,並且我們要保證每個子串在加進當前這個串的時候只被加進去一次,所以我們記錄每個子串最近一次更新答案是在哪一個字串加入的時候。然後對於每次詢問,我們拿出當前串,像AC自動機一樣在後綴自動機上跑匹配,不斷地往表示當前字首的點走,走完之後最後所在的點累加的權值就是這個串的答案。

程式碼:

#include <bits/stdc++.h>
using namespace std;

int n,q,T,vis[500010],fa[500010],len[500010],ch[500010][27],lst=1,cnt=1,rt=1,val[500010];
char s[500010];
inline void insert(int x,int id)
{
    int cur=++cnt,pre=lst;
    lst=cur;
    len[cur]=len[pre]+1;
    for(;pre&&!ch[pre][x];pre=fa[pre])
    ch[pre]
[x]=cur; if(!pre) fa[cur]=rt; else { int ji=ch[pre][x]; if(len[ji]==len[pre]+1) fa[cur]=ji; else { int gg=++cnt; len[gg]=len[pre]+1; memcpy(ch[gg],ch[ji],sizeof(ch[ji])); vis[gg]=vis[ji]; val[gg]=val[ji]; fa[gg]=fa[ji]; fa[ji]=fa[cur]=gg; for(;pre&&ch[pre][x]==ji;pre=fa[pre]) ch[pre][x]=gg; } } pre=cur; for(;pre&&vis[pre]!=id;pre=fa[pre]) { ++val[pre]; vis[pre]=id; } } int main() { scanf("%d%d",&T,&q); while(T) { scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;++i) insert(s[i]-'a',T); insert(26,T); T--; } while(q--) { scanf("%s",s+1); n=strlen(s+1); int ji=rt; for(int i=1;i<=n;++i) { int x=s[i]-'a'; ji=ch[ji][x]; } printf("%d\n",val[ji]); } return 0; }