bzoj - 2754 [SCOI2012]喵星球上的点名【后缀数组】+【RMQ】+【二分】+【离线】+【树状数组】

本文详细解析了SCOI2012竞赛中的喵星球点名问题,介绍了如何使用后缀数组解决复杂字符串匹配问题,包括统计点名响应次数和每个喵星人被点名的频率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2754: [SCOI2012]喵星球上的点名

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 3171  Solved: 1481
[Submit][Status][Discuss]

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
【提示】
事实上样例给出的数据如果翻译成地球上的语言可以这样来看
2 3
izayoi sakuya
orihara izaya
izay
hara
raiz

HINT

 



【数据范围】

 对于30%的数据,保证:

1<=N,M<=1000,喵星人的名字总长不超过4000,点名串的总长不超过2000。

对于100%的数据,保证:

1<=N<=20000,1<=M<=50000,喵星人的名字总长和点名串的总长分别不超过100000,保证喵星人的字符串中作为字符存在的数不超过10000。

 

 

分析:考虑后缀数组的做法。

首先把所有名字和询问都串起来建一个后缀数组,那么对于每个询问,可以左右二分一下lcp>=len,找出所有该串出现的位置,那么对于每个询问就相当于是询问一个区间,第一问是求这个区间内有多少种不同的数字,第二问是求每种数字被多少个不同的区间覆盖过。

第一问:把询问离线,预处理出每个数下一次出现的pos,开始询问之前,把所有不同的数第一次出现的地方都+1,当目前询问到的区间的左端点超过了某个点,就需要把这个点的nxt位置+1,然后直接查询该区间的和就可以了,这样可以保证每个点在同一个区间内只被计算一次。

第二问:同样把询问离线,再预处理处每个数上一次出现的pos,从第一个点遍历到最后一个点,对于一个区间[L,R],当遍历到L的时候,在L处+1,当遍历到R+1时,再在L处-1,然后ans[a[i]]就加上a[i]上一次出现的位置+1到i的区间和。

区间和直接用bit维护就可以了。

#include "bits/stdc++.h"
using namespace std;
const int maxn = 5e5;
int belong[maxn], pos[maxn], len[maxn], ans1[maxn], ans2[maxn], now[maxn], nxt[maxn], las[maxn];
pair<pair<int, int>, int> q[maxn];
vector<int> add[maxn], del[maxn];
struct BIT {
    int n;
    vector<int> v;
    BIT(int n) : n(n) { v.resize(n + 1); }
    void init(int n) {
        v.resize(n + 1);
        for (int i = 0; i <= n; ++i)v[i] = 0;
    }
    void update(int x, int d) {
        while (x <= n)v[x] += d, x += (x & -x);
    }
    int que(int x) {
        int res = 0;
        while (x > 0)res += v[x], x -= (x & -x);
        return res;
    }
} bit(maxn);
struct hasaki {
    int n, m, s[maxn], sa[maxn], x[maxn], y[maxn], c[maxn], height[maxn], rk[maxn], dp[maxn][40];
    void Suffix() {
        for (int i = 0; i < m; i++)c[i] = 0;
        for (int i = 0; i < n; i++)c[x[i] = s[i]]++;
        for (int i = 0; i < m; i++)c[i] += c[i - 1];
        for (int i = n - 1; i >= 0; i--)sa[--c[x[i]]] = i;
        for (int k = 1; k <= n; k <<= 1) {
            int p = 0;
            for (int i = n - k; i < n; i++)y[p++] = i;
            for (int i = 0; i < n; i++) if (sa[i] >= k) y[p++] = sa[i] - k;
            for (int i = 0; i < m; i++) c[i] = 0;
            for (int i = 0; i < n; i++) c[x[y[i]]]++;
            for (int i = 0; i < m; i++) c[i] += c[i - 1];
            for (int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
            swap(x, y);
            p = 1;
            x[sa[0]] = 0;
            for (int i = 1; i < n; i++)
                x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p - 1 : p++;
            if (p >= n) break;
            m = p;
        }
    }
    void getheight() {
        for (int i = 0; i < n; i++) rk[sa[i]] = i;
        for (int i = 0, k = 0; i < n; i++) {
            if (rk[i]) {
                if (k) --k;
                else k = 0;
                int j = sa[rk[i] - 1];
                while (s[i + k] == s[j + k])k++;
                height[rk[i]] = k;
            }
        }
    }
    void init() {
        for (int i = 0; i < n; ++i)dp[i][0] = height[i];
        for (int j = 1; (1 << j) < n; ++j) {
            for (int i = 0; i + (1 << j) - 1 < n; ++i) {
                dp[i][j] = 0x3f3f3f3f;
                dp[i][j] = min(dp[i][j - 1], dp[i + (1 << j - 1)][j - 1]);
            }
        }
    }
    int que(int l, int r) {
        if (l > r)swap(l, r);
        l++;
        int k = log2(r - l + 1);
        return min(dp[l][k], dp[r - (1 << k) + 1][k]);
    }
} shit;
int main() {
    int n, m, x, l;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &l);
        while (l--)scanf("%d", &x), belong[shit.n] = i + 1, shit.s[shit.n++] = x;
        shit.s[shit.n++] = 1e4 + 1;
        scanf("%d", &l);
        while (l--)scanf("%d", &x), belong[shit.n] = i + 1, shit.s[shit.n++] = x;
        shit.s[shit.n++] = 1e4 + 1;
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", &l), len[i] = l, pos[i] = shit.n;
        while (l--)scanf("%d", &x), shit.s[shit.n++] = x;
        shit.s[shit.n++] = 1e4 + 1;
    }
    shit.m = 10002, shit.Suffix(), shit.getheight(), shit.init();
    for (int i = 0; i < m; ++i) {
        int l, r, ansl = shit.rk[pos[i]], ansr = shit.rk[pos[i]];
        l = 0, r = ansl - 1;
        while (l <= r) {
            int mid = l + r >> 1;
            if (shit.que(mid, shit.rk[pos[i]]) >= len[i]) {
                ansl = mid;
                r = mid - 1;
            } else l = mid + 1;
        }
        l = ansr + 1, r = shit.n - 1;
        while (l <= r) {
            int mid = l + r >> 1;
            if (shit.que(mid, shit.rk[pos[i]]) >= len[i]) {
                ansr = mid;
                l = mid + 1;
            } else r = mid - 1;
        }
        q[i] = make_pair(make_pair(ansl, ansr), i);
    }
    sort(q, q + m);
    memset(now, -1, sizeof(now));
    memset(nxt, -1, sizeof(nxt));
    memset(las, -1, sizeof(las));
    for (int i = shit.n - 1; i >= 0; --i) {
        int x = belong[shit.sa[i]];
        if (x == 0)continue;
        if (now[x] != -1)nxt[i + 1] = now[x];
        now[x] = i + 1;
    }
    memset(now, -1, sizeof(now));
    for (int i = 0; i < shit.n; ++i) {
        x = belong[shit.sa[i]];
        if (x == 0)continue;
        if (now[x] != -1)las[i + 1] = now[x];
        else bit.update(i + 1, 1);
        now[x] = i;
    }
    int laast = 0;
    for (int i = 0; i < m; ++i) {
        add[q[i].first.first + 1].push_back(1);
        del[q[i].first.second + 2].push_back(q[i].first.first + 1);
        while (laast <= q[i].first.first) {
            if (nxt[laast] != -1)bit.update(nxt[laast], 1);
            laast++;
        }
        ans1[q[i].second] = bit.que(q[i].first.second + 1) - bit.que(q[i].first.first);
    }
    bit.init(maxn);
    for (int i = 1; i <= shit.n; ++i) {
        bit.update(i, add[i].size());
        for (int j = 0; j < del[i].size(); ++j)bit.update(del[i][j], -1);
        int last = max(las[i] + 1, 0);
        ans2[belong[shit.sa[i - 1]]] += bit.que(i) - bit.que(last);
    }
    for (int i = 0; i < m; ++i)printf("%d\n", ans1[i]);
    for (int i = 1; i <= n; ++i)printf("%d ", ans2[i]);
}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值