BZOJ.2780.[SPOJ8093]Sevenk Love Oimaster(廣義後綴自動機)
阿新 • • 發佈:2018-06-28
tps cstring == 字母 ima 我們 模式串 小寫 suffix
題目鏈接
\(Description\)
給定n個模式串,多次詢問一個串在多少個模式串中出現過。(字符集為26個小寫字母)
\(Solution\)
對每個詢問串進行匹配最終會達到一個節點,我們需要得到這個節點所代表的子串出現在多少個模式串中。
建立廣義後綴自動機。每次插入一個串,從root開始,對於SAM上每個節點維護cnt和bef,分別表示該節點代表的串出現在幾個模式串中 和 該節點最近被哪個模式串更新過cnt。
對於bef[i]!=now的節點,++cnt[i],bef[i]=now;當模式串now下次匹配到當前節點時則不再更新。
另外,如果匹配了當前節點i那麽一定會匹配上fa[i],fa[fa[i]]...如果它們的bef[]!=now,則都更新一遍。直到有個節點p滿足bef[p]==now,那麽就不需要再向上更新了(再往上已經更新過了)。(這個在insert後用np更新就可以啊)
註意新建nq時 bef[nq],cnt[nq]也要復制(=...[q])。
仰望Rank1(還是後綴自動機第一題的Rank1),落後面人一條街。
//24612kb 76ms
#include <cstdio>
#include <cstring>
#include <algorithm>
#define gc() getchar()
const int N=2e5+5;
struct Suffix_Automaton
{
int las,tot,fa[N],son[N][26],len[N],cnt[N],bef[N];
char s[360005 ];
void Init(){
las=tot=1;
}
void Insert(int now,int c)
{
int p=las,np=++tot; len[las=np]=len[p]+1;
for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;
if(!p) fa[np]=1;
else
{
int q=son[p][c];
if(len[q]==len[p]+1 ) fa[np]=q;
else
{
int nq=++tot;
len[nq]=len[p]+1, bef[nq]=bef[q], cnt[nq]=cnt[q];//!
memcpy(son[nq],son[q],sizeof son[q]);
fa[nq]=fa[q], fa[q]=fa[np]=nq;
for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;
}
}
for(; bef[np]!=now&&np; np=fa[np])
++cnt[np], bef[np]=now;
}
void Build(int now)
{
las=1, scanf("%s",s);
for(int i=0,l=strlen(s); i<l; ++i)
Insert(now,s[i]-'a');
}
void Query()
{
int p=1; scanf("%s",s);
for(int i=0,l=strlen(s); i<l&&p; ++i)
p=son[p][s[i]-'a'];
printf("%d\n",cnt[p]);
}
}sam;
int main()
{
int n,Q; scanf("%d%d",&n,&Q); sam.Init();
for(int i=1; i<=n; ++i) sam.Build(i);
while(Q--) sam.Query();
return 0;
}
BZOJ.2780.[SPOJ8093]Sevenk Love Oimaster(廣義後綴自動機)