bzoj-2754[SCOI2012]喵星球上的點名——字尾自動機
阿新 • • 發佈:2021-02-06
其他人寫的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]);
}