1. 程式人生 > 其它 >bzoj-2754[SCOI2012]喵星球上的點名——字尾自動機

bzoj-2754[SCOI2012]喵星球上的點名——字尾自動機

其他人寫的SAM題解我都看不懂,而且廣義SAM的構建都是錯誤的構建方法,只能自己寫一篇構建廣義SAM正確,但是又比較暴力可以過的題解了。
題解:
第一問很簡單,求詢問的子串在多少個姓名中出現過,只要每次加進一個字元進去的時候,我們都對該字元的及其祖先進行標記此姓名出現過即可。
第二個問題的話,其實在標記第一問的同時可以用一個vector去標記該節點出現過那些姓名就行了。(當然又更優的解法,這個方法空間開銷太大了)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue> #include<stack> #include<map> #include<set> #include<ctime> #include<algorithm> #include<sstream> #include<bitset> #define scand(a) scanf("%d",&a) #define scandd(a,b) scanf("%d%d",&a,&b) #define scanddd(
a,b,c) scanf("%d%d%d",&a,&b,&c) #define mst(a,b) memset(a,b,sizeof(a)) #define lowbit(x) (x&-x) #define lson rt<<1 #define rson rt<<1|1 using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<ll,
ll> pll; const int inf=0x3f3f3f3f; const ll INF=0x3f3f3f3f3f3f3f3fLL; const int maxn=2e5+5; const ll mod=2012; map<int,int>ch[maxn]; int fa[maxn],len[maxn],tot=1,last=1; //int c[maxn],cnt[maxn]; int nxt[maxn]; int n,m; int sum[maxn]; vector<int>vis[maxn]; void extend(int c) { if(ch[last].find(c)!=ch[last].end()) { int p=last,x=ch[p][c]; if(len[p]+1==len[x])last=x; else{ int y=++tot; len[y]=len[p]+1; ch[y]=ch[x]; nxt[y]=nxt[x];//廣義sam記錄節點的話這裡記得賦值 sum[y]=sum[x];// vis[y]=vis[x];// //memcpy(ch[y],ch[x],sizeof(ch[x])); while(p&&ch[p][c]==x)ch[p][c]=y,p=fa[p]; fa[y]=fa[x]; last=fa[x]=y; } return; } int now=++tot,pre=last; last=now,len[now]=len[pre]+1; while(pre&&ch[pre].find(c)==ch[pre].end()) { ch[pre][c]=now; pre=fa[pre]; } if(!pre)fa[now]=1; else { int x=ch[pre][c]; if(len[x]==len[pre]+1)fa[now]=x; else { int y=++tot; ch[y]=ch[x]; nxt[y]=nxt[x];//同上 sum[y]=sum[x]; vis[y]=vis[x]; //memcpy(ch[y],ch[x],sizeof(ch[x])); len[y]=len[pre]+1; fa[y]=fa[x]; fa[x]=fa[now]=y; while(pre&&ch[pre][c]==x) { ch[pre][c]=y; pre=fa[pre]; } } } } //void sort() //{ // for(int i=0;i<=tot;i++)cnt[i]=c[i]=0; // for(int i=1;i<=tot;i++)cnt[len[i]]++; // for(int i=1;i<=tot;i++)cnt[i]+=cnt[i-1]; // for(int i=1;i<=tot;i++)c[cnt[len[i]]--]=i; // for(int i=tot;i>=1;i--)sum[fa[c[i]]]+=sum[c[i]]; //} //void init() //{ // tot=last=1; // mst(ch,0); // mst(len,0); // mst(sum,0); //} void inp(int now) { int len; scand(len); for(int j=0;j<len;j++) { int t; scand(t); extend(t); int p=last; while(p) { if(nxt[p]!=now) { sum[p]++; nxt[p]=now; vis[p].push_back(now); } p=fa[p]; } } } int ans[maxn]; int main() { #ifdef local freopen("1.txt","r",stdin); freopen("2.txt","w",stdout); #endif scandd(n,m); for(int i=1;i<=n;i++) { last=1; inp(i); last=1; inp(i); } for(int i=1;i<=m;i++) { int len; scand(len); int p=1; for(int j=1;j<=len;j++) { int t; scand(t); p=ch[p][t]; } printf("%d\n",sum[p]); for(int j=0;j<vis[p].size();j++)//對該節點存在的姓名計數就行 { int v=vis[p][j]; ans[v]++; } } for(int i=1;i<=n;i++)printf("%d ",ans[i]); }