BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机/后缀自动机)
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
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
0
1 2
解题思路:
1、AC自动机
首先,我是从来不用AC自动机的,直到看到了这道题。
Trie图构建时需要承受枚举字符集的复杂度,在匹配{a~z}时还只是一个26的常数在这道题变得不可承受。
所以就不能进行图没有儿子时的构建。
那么就是AC自动机了。
map存儿子。
vector存名字。
很少使用STL选手当场死亡。
向dalao请教map枚举元素的方法。
结果一直WA
最后才知道可能会有重复的询问QAQ拿vector存一下。
致敬hzwer学长·。
代码:
1 #include<map> 2 #include<queue> 3 #include<cstdio> 4 #include<vector> 5 #include<cstring> 6 #include<algorithm> 7 char xB[1<<15],*xS=xB,*xTT=xB; 8 #define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++) 9 #define isd(c) (c>='0'&&c<='9') 10 inline int read(){ 11 char xchh; 12 int xaa; 13 while(xchh=getc(),!isd(xchh));(xaa=xchh-'0'); 14 while(xchh=getc(),isd(xchh))xaa=xaa*10+xchh-'0'; 15 return xaa; 16 } 17 using std::map; 18 using std::queue; 19 using std::vector; 20 typedef unsigned int uit; 21 struct trnt{ 22 map<int,int>ch; 23 int fl; 24 vector<int>fin; 25 int vis; 26 }tr[600000]; 27 struct idt{ 28 int la,lb; 29 vector<int>a; 30 vector<int>b; 31 int ans; 32 void Ins(void) 33 { 34 scanf("%d",&la); 35 for(int i=1;i<=la;i++) 36 { 37 int tt; 38 scanf("%d",&tt); 39 a.push_back(tt); 40 } 41 scanf("%d",&lb); 42 for(int i=1;i<=lb;i++) 43 { 44 int tt; 45 scanf("%d",&tt); 46 b.push_back(tt); 47 } 48 return ; 49 } 50 }cat[1000000]; 51 int num[1000000]; 52 bool usd[1000000]; 53 int n,m; 54 int siz; 55 int len; 56 queue<int>Q; 57 vector<int>hre; 58 void Insert(int k) 59 { 60 int len; 61 int c; 62 int root=0; 63 scanf("%d",&len); 64 for(int i=1;i<=len;i++) 65 { 66 scanf("%d",&c); 67 if(tr[root].ch.find(c)==tr[root].ch.end()) 68 tr[root].ch[c]=++siz; 69 root=tr[root].ch[c]; 70 } 71 tr[root].fin.push_back(k); 72 return ; 73 } 74 void Build(void) 75 { 76 int root=0; 77 for(map<int,int>::iterator j=tr[root].ch.begin();j!=tr[root].ch.end();j++) 78 { 79 int sn=j->second; 80 tr[sn].fl=0; 81 Q.push(sn); 82 } 83 while(!Q.empty()) 84 { 85 root=Q.front(); 86 Q.pop(); 87 for(map<int,int>::iterator j=tr[root].ch.begin();j!=tr[root].ch.end();j++) 88 { 89 int i=j->first; 90 int sn=j->second; 91 int nxt=tr[root].fl; 92 while(nxt&&tr[nxt].ch.find(i)==tr[nxt].ch.end()) 93 nxt=tr[nxt].fl; 94 if(tr[nxt].ch.find(i)!=tr[nxt].ch.end()) 95 nxt=tr[nxt].ch[i]; 96 tr[sn].fl=nxt; 97 Q.push(sn); 98 } 99 } 100 return; 101 } 102 void Query(int p) 103 { 104 int root; 105 root=0; 106 for(uit i=0;i<cat[p].a.size();i++) 107 { 108 int c=cat[p].a[i]; 109 while(root&&tr[root].ch.find(c)==tr[root].ch.end()) 110 root=tr[root].fl; 111 if(tr[root].ch.find(c)!=tr[root].ch.end()) 112 { 113 root=tr[root].ch[c]; 114 int nxt=root; 115 while(nxt) 116 { 117 if(tr[nxt].vis==p) 118 break; 119 tr[nxt].vis=p; 120 for(uit l=0;l<tr[nxt].fin.size();l++) 121 { 122 int f=tr[nxt].fin[l]; 123 if(!usd[f]) 124 { 125 usd[f]=true; 126 hre.push_back(f); 127 cat[p].ans++; 128 num[f]++; 129 } 130 } 131 nxt=tr[nxt].fl; 132 } 133 } 134 } 135 root=0; 136 for(uit i=0;i<cat[p].b.size();i++) 137 { 138 int c=cat[p].b[i]; 139 while(root&&tr[root].ch.find(c)==tr[root].ch.end()) 140 root=tr[root].fl; 141 if(tr[root].ch.find(c)!=tr[root].ch.end()) 142 { 143 root=tr[root].ch[c]; 144 int nxt=root; 145 while(nxt) 146 { 147 if(tr[nxt].vis==p) 148 break; 149 tr[nxt].vis=p; 150 for(uit l=0;l<tr[nxt].fin.size();l++) 151 { 152 int f=tr[nxt].fin[l]; 153 if(!usd[f]) 154 { 155 usd[f]=true; 156 hre.push_back(f); 157 cat[p].ans++; 158 num[f]++; 159 } 160 } 161 nxt=tr[nxt].fl; 162 } 163 } 164 } 165 for(uit i=0;i<hre.size();i++) 166 usd[hre[i]]=false; 167 hre.clear(); 168 return ; 169 } 170 int main() 171 { 172 scanf("%d%d",&n,&m); 173 for(int i=1;i<=n;i++) 174 cat[i].Ins(); 175 for(int i=1;i<=m;i++) 176 Insert(i); 177 Build(); 178 for(int i=1;i<=n;i++) 179 Query(i); 180 for(int i=1;i<=m;i++) 181 printf("%d\n",num[i]); 182 for(int i=1;i<=n;i++) 183 printf("%d ",cat[i].ans); 184 return 0; 185 }
2.广义后缀自动机
只需要先建广义后缀自动机,在pre节点上打好标记,在查询时查询节点的标记就好了。 第二问只需在询问结束节点打标记遍历子串就好了。
代码:
1 #include<map> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 struct sant{ 6 std::map<int,int>tranc; 7 int len; 8 int pre; 9 }s[2000000]; 10 struct pnt{ 11 int wgt; 12 int vis; 13 int tms; 14 }p[2000000]; 15 int cnt; 16 int siz; 17 int fin; 18 int n,m; 19 int lenfname[1000000]; 20 int lensname[100000]; 21 int str[100001]; 22 void Insert(int c) 23 { 24 int nwp,nwq,lsp,lsq; 25 nwp=++siz; 26 s[nwp].len=s[fin].len+1; 27 for(lsp=fin;lsp&&(s[lsp].tranc.find(c)==s[lsp].tranc.end());lsp=s[lsp].pre) 28 s[lsp].tranc[c]=nwp; 29 if(!lsp) 30 s[nwp].pre=1; 31 else{ 32 lsq=s[lsp].tranc[c]; 33 if(s[lsq].len==s[lsp].len+1) 34 s[nwp].pre=lsq; 35 else{ 36 nwq=++siz; 37 s[nwq]=s[lsq]; 38 s[nwq].len=s[lsp].len+1; 39 s[nwp].pre=s[lsq].pre=nwq; 40 while((s[lsp].tranc.find(c)!=s[lsp].tranc.end())&&s[lsp].tranc[c]==lsq) 41 { 42 s[lsp].tranc[c]=nwq; 43 lsp=s[lsp].pre; 44 } 45 } 46 } 47 fin=nwp; 48 return ; 49 } 50 void mark(int plc,int times) 51 { 52 if(!plc) 53 return ; 54 if(p[plc].vis==times) 55 return ; 56 p[plc].vis=times; 57 p[plc].wgt++; 58 mark(s[plc].pre,times); 59 return ; 60 } 61 int take(int plc,int times) 62 { 63 if(!plc) 64 return 0; 65 if(p[plc].vis==times) 66 return 0; 67 p[plc].vis=times; 68 return p[plc].tms+take(s[plc].pre,times); 69 } 70 int main() 71 { 72 fin=++siz; 73 scanf("%d%d",&n,&m); 74 for(int i=1;i<=n;i++) 75 { 76 fin=1; 77 scanf("%d",&lenfname[i]); 78 for(int j=1;j<=lenfname[i];j++) 79 { 80 cnt++; 81 scanf("%d",&str[cnt]); 82 Insert(str[cnt]); 83 } 84 fin=1; 85 scanf("%d",&lensname[i]); 86 for(int j=1;j<=lensname[i];j++) 87 { 88 cnt++; 89 scanf("%d",&str[cnt]); 90 Insert(str[cnt]); 91 } 92 } 93 cnt=0; 94 for(int i=1;i<=n;i++) 95 { 96 int root; 97 root=1; 98 for(int j=1;j<=lenfname[i];j++) 99 { 100 cnt++; 101 root=s[root].tranc[str[cnt]]; 102 mark(root,i); 103 } 104 root=1; 105 for(int j=1;j<=lensname[i];j++) 106 { 107 cnt++; 108 root=s[root].tranc[str[cnt]]; 109 mark(root,i); 110 } 111 } 112 for(int i=1;i<=m;i++) 113 { 114 int len; 115 int tmp; 116 scanf("%d",&len); 117 int root=1; 118 for(int j=1;j<=len;j++) 119 { 120 scanf("%d",&tmp); 121 if(!root) 122 continue; 123 if(s[root].tranc.find(tmp)!=s[root].tranc.end()) 124 root=s[root].tranc[tmp]; 125 else 126 root=0; 127 } 128 printf("%d\n",p[root].wgt); 129 p[root].tms++; 130 } 131 cnt=0; 132 for(int i=1;i<=n;i++) 133 { 134 int ans=0; 135 int root; 136 root=1; 137 for(int j=1;j<=lenfname[i];j++) 138 { 139 cnt++; 140 root=s[root].tranc[str[cnt]]; 141 ans+=take(root,i+n); 142 } 143 root=1; 144 for(int j=1;j<=lensname[i];j++) 145 { 146 cnt++; 147 root=s[root].tranc[str[cnt]]; 148 ans+=take(root,i+n); 149 } 150 printf("%d ",ans); 151 } 152 puts(""); 153 return 0; 154 }