【SCOI2012】喵星球上的点名
Description
给出n个模式串,m个文本串,每个模式串由两部分组成,我们认为一个模式串被一个文本串包含只要这个文本串包含它的两部分中的其中一部分的子串。求每个文本串包含多少个模式串,每个模式串又被多少个文本串包含。
1≤n≤20000,1≤m≤50000,模式串总长和文本串的总长分别不超过100000 保证模式串和文本串中作为字符存在的数不超过10000。
Solution
标准的字符串匹配神题。
可以用AC自动机水过。
字符集很大,c++开map即可。
发现文本串只有一部分,就把它建树,对于匹配的某个节点延失配指针暴力统计答案即可。
正解似乎是SA+Chairman_tree,但由于有总长限制似乎不好卡掉。
又有谁会那么无聊来到处乱HACK呢~~
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 20005
#define M 50005
#define Len 100005
using namespace std;
typedef vector<int> vec;
typedef map<int,int>:: iterator it;
map<int,int> trie[Len];
vec a[N],b[N],p[Len];
int d[Len],next[Len],ans1[M],ans2[N],h[M],x,tot,n,m,len,bz;
void makefail() {
int i=0,j=1;
while (i<j) {
for(it ii=trie[d[++i]].begin();ii!=trie[d[i]].end();ii++) {
int k=ii->first;x=trie[d[i]][k];
if (d[i]) {
int y=next[d[i]];
while (y&&!trie[y][k]) y=next[y];
next[x]=trie[y][k];
}d[++j]=x;
}
}
}
int find(vec a) {
int l=a.size()-1;x=0;int z=0;
fo(i,0,l) {
while (x&&!trie[x][a[i]]) x=next[x];
x=trie[x][a[i]];
for(int y=x;y;y=next[y])
if (!p[y].empty())
fo(j,0,p[y].size()-1)
if (h[p[y][j]]!=bz) {
h[p[y][j]]=bz;z++;ans1[p[y][j]]++;
}
}
return z;
}
int main() {
scanf("%d%d",&n,&m);
fo(i,1,n) {
scanf("%d",&len);
fo(j,1,len) scanf("%d",&x),a[i].push_back(x);
scanf("%d",&len);
fo(j,1,len) scanf("%d",&x),b[i].push_back(x);
}
fo(i,1,m) {
scanf("%d",&len);int y=0;
fo(j,1,len) {
scanf("%d",&x);
if (!trie[y][x]) trie[y][x]=++tot;
y=trie[y][x];
}p[y].push_back(i);
}
makefail();
fo(i,1,n) {
++bz;ans2[i]=find(a[i])+find(b[i]);
}
fo(i,1,m) printf("%d\n",ans1[i]);
fo(i,1,n) printf("%d ",ans2[i]);
}