Description

a180285幸运地被选做了地球到喵星球的留学生。他发现喵星人在上课前的点名现象非常有趣。   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成。喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这个喵星人就必须答到。 然而,由于喵星人的字码过于古怪,以至于不能用ASCII码来表示。为了方便描述,a180285决定用数串来表示喵星人的名字。
现在你能帮助a180285统计每次点名的时候有多少喵星人答到,以及M次点名结束后每个喵星人答到多少次吗?  

Input

 
现在定义喵星球上的字符串给定方法:
先给出一个正整数L,表示字符串的长度,接下来L个整数表示字符串的每个字符。
输入的第一行是两个整数N和M。
接下来有N行,每行包含第i 个喵星人的姓和名两个串。姓和名都是标准的喵星球上的
字符串。
接下来有M行,每行包含一个喵星球上的字符串,表示老师点名的串。

Output

 
对于每个老师点名的串输出有多少个喵星人应该答到。
然后在最后一行输出每个喵星人被点到多少次。

Sample Input

2 3
6 8 25 0 24 14 8 6 18 0 10 20 24 0
7 14 17 8 7 0 17 0 5 8 25 0 24 0
4 8 25 0 24
4 7 0 17 0
4 17 0 8 25

Sample Output

2
1
0
1 2

解题思路:

1、AC自动机

首先,我是从来不用AC自动机的,直到看到了这道题。

Trie图构建时需要承受枚举字符集的复杂度,在匹配{a~z}时还只是一个26的常数在这道题变得不可承受。

所以就不能进行图没有儿子时的构建。

那么就是AC自动机了。

map存儿子。

vector存名字。

很少使用STL选手当场死亡。

向dalao请教map枚举元素的方法。

结果一直WA

最后才知道可能会有重复的询问QAQ拿vector存一下。

致敬hzwer学长·。

代码:

 #include<map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
char xB[<<],*xS=xB,*xTT=xB;
#define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++)
#define isd(c) (c>='0'&&c<='9')
inline int read(){
char xchh;
int xaa;
while(xchh=getc(),!isd(xchh));(xaa=xchh-'');
while(xchh=getc(),isd(xchh))xaa=xaa*+xchh-'';
return xaa;
}
using std::map;
using std::queue;
using std::vector;
typedef unsigned int uit;
struct trnt{
map<int,int>ch;
int fl;
vector<int>fin;
int vis;
}tr[];
struct idt{
int la,lb;
vector<int>a;
vector<int>b;
int ans;
void Ins(void)
{
scanf("%d",&la);
for(int i=;i<=la;i++)
{
int tt;
scanf("%d",&tt);
a.push_back(tt);
}
scanf("%d",&lb);
for(int i=;i<=lb;i++)
{
int tt;
scanf("%d",&tt);
b.push_back(tt);
}
return ;
}
}cat[];
int num[];
bool usd[];
int n,m;
int siz;
int len;
queue<int>Q;
vector<int>hre;
void Insert(int k)
{
int len;
int c;
int root=;
scanf("%d",&len);
for(int i=;i<=len;i++)
{
scanf("%d",&c);
if(tr[root].ch.find(c)==tr[root].ch.end())
tr[root].ch[c]=++siz;
root=tr[root].ch[c];
}
tr[root].fin.push_back(k);
return ;
}
void Build(void)
{
int root=;
for(map<int,int>::iterator j=tr[root].ch.begin();j!=tr[root].ch.end();j++)
{
int sn=j->second;
tr[sn].fl=;
Q.push(sn);
}
while(!Q.empty())
{
root=Q.front();
Q.pop();
for(map<int,int>::iterator j=tr[root].ch.begin();j!=tr[root].ch.end();j++)
{
int i=j->first;
int sn=j->second;
int nxt=tr[root].fl;
while(nxt&&tr[nxt].ch.find(i)==tr[nxt].ch.end())
nxt=tr[nxt].fl;
if(tr[nxt].ch.find(i)!=tr[nxt].ch.end())
nxt=tr[nxt].ch[i];
tr[sn].fl=nxt;
Q.push(sn);
}
}
return;
}
void Query(int p)
{
int root;
root=;
for(uit i=;i<cat[p].a.size();i++)
{
int c=cat[p].a[i];
while(root&&tr[root].ch.find(c)==tr[root].ch.end())
root=tr[root].fl;
if(tr[root].ch.find(c)!=tr[root].ch.end())
{
root=tr[root].ch[c];
int nxt=root;
while(nxt)
{
if(tr[nxt].vis==p)
break;
tr[nxt].vis=p;
for(uit l=;l<tr[nxt].fin.size();l++)
{
int f=tr[nxt].fin[l];
if(!usd[f])
{
usd[f]=true;
hre.push_back(f);
cat[p].ans++;
num[f]++;
}
}
nxt=tr[nxt].fl;
}
}
}
root=;
for(uit i=;i<cat[p].b.size();i++)
{
int c=cat[p].b[i];
while(root&&tr[root].ch.find(c)==tr[root].ch.end())
root=tr[root].fl;
if(tr[root].ch.find(c)!=tr[root].ch.end())
{
root=tr[root].ch[c];
int nxt=root;
while(nxt)
{
if(tr[nxt].vis==p)
break;
tr[nxt].vis=p;
for(uit l=;l<tr[nxt].fin.size();l++)
{
int f=tr[nxt].fin[l];
if(!usd[f])
{
usd[f]=true;
hre.push_back(f);
cat[p].ans++;
num[f]++;
}
}
nxt=tr[nxt].fl;
}
}
}
for(uit i=;i<hre.size();i++)
usd[hre[i]]=false;
hre.clear();
return ;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
cat[i].Ins();
for(int i=;i<=m;i++)
Insert(i);
Build();
for(int i=;i<=n;i++)
Query(i);
for(int i=;i<=m;i++)
printf("%d\n",num[i]);
for(int i=;i<=n;i++)
printf("%d ",cat[i].ans);
return ;
}

2.广义后缀自动机

只需要先建广义后缀自动机,在pre节点上打好标记,在查询时查询节点的标记就好了。 第二问只需在询问结束节点打标记遍历子串就好了。

代码:

 #include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
struct sant{
std::map<int,int>tranc;
int len;
int pre;
}s[];
struct pnt{
int wgt;
int vis;
int tms;
}p[];
int cnt;
int siz;
int fin;
int n,m;
int lenfname[];
int lensname[];
int str[];
void Insert(int c)
{
int nwp,nwq,lsp,lsq;
nwp=++siz;
s[nwp].len=s[fin].len+;
for(lsp=fin;lsp&&(s[lsp].tranc.find(c)==s[lsp].tranc.end());lsp=s[lsp].pre)
s[lsp].tranc[c]=nwp;
if(!lsp)
s[nwp].pre=;
else{
lsq=s[lsp].tranc[c];
if(s[lsq].len==s[lsp].len+)
s[nwp].pre=lsq;
else{
nwq=++siz;
s[nwq]=s[lsq];
s[nwq].len=s[lsp].len+;
s[nwp].pre=s[lsq].pre=nwq;
while((s[lsp].tranc.find(c)!=s[lsp].tranc.end())&&s[lsp].tranc[c]==lsq)
{
s[lsp].tranc[c]=nwq;
lsp=s[lsp].pre;
}
}
}
fin=nwp;
return ;
}
void mark(int plc,int times)
{
if(!plc)
return ;
if(p[plc].vis==times)
return ;
p[plc].vis=times;
p[plc].wgt++;
mark(s[plc].pre,times);
return ;
}
int take(int plc,int times)
{
if(!plc)
return ;
if(p[plc].vis==times)
return ;
p[plc].vis=times;
return p[plc].tms+take(s[plc].pre,times);
}
int main()
{
fin=++siz;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
{
fin=;
scanf("%d",&lenfname[i]);
for(int j=;j<=lenfname[i];j++)
{
cnt++;
scanf("%d",&str[cnt]);
Insert(str[cnt]);
}
fin=;
scanf("%d",&lensname[i]);
for(int j=;j<=lensname[i];j++)
{
cnt++;
scanf("%d",&str[cnt]);
Insert(str[cnt]);
}
}
cnt=;
for(int i=;i<=n;i++)
{
int root;
root=;
for(int j=;j<=lenfname[i];j++)
{
cnt++;
root=s[root].tranc[str[cnt]];
mark(root,i);
}
root=;
for(int j=;j<=lensname[i];j++)
{
cnt++;
root=s[root].tranc[str[cnt]];
mark(root,i);
}
}
for(int i=;i<=m;i++)
{
int len;
int tmp;
scanf("%d",&len);
int root=;
for(int j=;j<=len;j++)
{
scanf("%d",&tmp);
if(!root)
continue;
if(s[root].tranc.find(tmp)!=s[root].tranc.end())
root=s[root].tranc[tmp];
else
root=;
}
printf("%d\n",p[root].wgt);
p[root].tms++;
}
cnt=;
for(int i=;i<=n;i++)
{
int ans=;
int root;
root=;
for(int j=;j<=lenfname[i];j++)
{
cnt++;
root=s[root].tranc[str[cnt]];
ans+=take(root,i+n);
}
root=;
for(int j=;j<=lensname[i];j++)
{
cnt++;
root=s[root].tranc[str[cnt]];
ans+=take(root,i+n);
}
printf("%d ",ans);
}
puts("");
return ;
}

BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机/后缀自动机)的更多相关文章

  1. BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机)

    Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 2816  Solved: 1246[Submit][Status][Discuss] Descript ...

  2. [BZOJ2754] [SCOI2012]喵星球上的点名解题报告|后缀数组

    a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的 ...

  3. BZOJ 2754: [SCOI2012]喵星球上的点名 [AC自动机+map+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1902  Solved: 837[Submit][St ...

  4. BZOJ2754: [SCOI2012]喵星球上的点名

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 680  Solved: 314[Submit][Sta ...

  5. bzoj2754:[SCOI2012]喵星球上的点名(后缀自动机)

    Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串 ...

  6. BZOJ 2754 [SCOI2012]喵星球上的点名 (AC自动机、树状数组)

    吐槽: 为啥很多人用AC自动机暴力跳都过了?复杂度真的对么? 做法一: AC自动机+树状数组 姓名的问题,中间加个特殊字符连起来即可. 肯定是对点名串建AC自动机(map存儿子),然后第一问就相当于问 ...

  7. BZOJ2754 SCOI2012喵星球上的点名

    绝世好题. 正当我犹豫不决时,hzwer说:“MAP!!!” 没错这题大大的暴力,生猛的stl,贼基尔爽,,ԾㅂԾ,, 由于我们求点名在名字中的子串个数,所以将点名建AC自动机,记录节点属于哪次点名, ...

  8. BZOJ 2754 [SCOI2012]喵星球上的点名 (AC自动机+map维护Trie树)

    题目大意:略 由于字符集大,要用map维护Trie树 并不能用AC自动机的Trie图优化,不然内存会炸 所以我用AC自动机暴跳fail水过的 显然根据喵星人建AC自动机是不行的,所以要根据问题建 然而 ...

  9. BZOJ2754 [SCOI2012]喵星球上的点名 SA+莫队+树状数组

    题面 戳这里 题解 首先先把所有给出的姓名和询问全部接在一起,建出\(height\)数组. 某个串要包含整个询问串,其实就相当于某个串与询问串的\(lcp\)为询问串的长度. 而两个后缀\(Suff ...

随机推荐

  1. Hive Cilent数据操作

    Hive运行命令方式有cli,jdbc.hwi.beeline.而我们经常使用的往往是cli shell 操作. cli shell hive -help hive --help 注:命令脚本必须在集 ...

  2. JSP脚本连接数据库

    入门 简单的jsp文件 <%-- Created by IntelliJ IDEA. User: e550 Date: 2017/1/9 Time: 23:24 To change this t ...

  3. HIToj--1076--Ordered Fractions(水题)

    Ordered Fractions   Source : Unknown   Time limit : 3 sec   Memory limit : 32 M Submitted : 1510, Ac ...

  4. spring mvc获取路径参数的几种方式

    一.从视图向controller传递值,  controller <--- 视图 1.通过@PathVariabl注解获取路径中传递参数 (参数会被复制到路径变量) @RequestMappin ...

  5. Introspector

    import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; im ...

  6. SQL 自增列清零方法

    SQL Identity自增列清零方法1.使用DBCC控制台命令: dbcc checkident(表名,RESEED,0) 2.truncate table 也可将当前标识值清零 但当有外键等约束时 ...

  7. vector与deque的区别

    最重要的区别,是内部实现上.deque是分段存储的. 都是支持随机存取. http://www.cnblogs.com/zhuyf87/archive/2012/12/09/2809896.html ...

  8. Linux中配置网桥

    使用kvm虚拟机时,有时候需要自己添加网桥供guest使用. 不使用libvirt来管理的话,可以使用以下方法创建网桥并绑定到物理网卡(RHEL6/Fedora已实验): 1.创建网桥配置文件ifcf ...

  9. eXtremeDB相关问题解答(3)

    > 1.         Could our database support multi-database under one single instance? >   > 2. ...

  10. EC2 开启 IPV6 访问 和 禁止重启后自动分配IP地址

    EC2 开启 IPV6 访问 和 禁止重启后自动分配IP地址进入 VPC 控制台,对当前 VPC 添加 IPV6 CIDR 块对该 VPC 的路由表进行修改,添加其它路由,第一个空填::/0,第二个空 ...