【SCOI2012】喵星球上的点名

7 篇文章 0 订阅
5 篇文章 0 订阅

【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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值