SCOI2012 喵星球上的点名

本文介绍了一种使用AC自动机解决字符串匹配问题的方法。通过为询问串建立AC自动机,并将每个输入串在该自动机上进行匹配,实现对多个模式串的同时搜索。文章提供了一份详细的代码实现,包括自动机的构建过程、失败指针的设置以及具体的查询操作。

题意就不说了, 反正是中文;
正解应该是后缀数组, 但是AC自动机可以强行过, 只是比较慢;
对于询问串建AC自动机, 然后暴力把每个喵星人暴力在树上跑, 然后。。。就没有然后了。

#include <bits/stdc++.h>

const int mx_n = 2e4 + 10;
const int mx_m = 5e4 + 10;
const int mx_node = 1e5 + 10;
#define pb push_back
#define rep(i, s, t) for(register int i = s; i <= t; ++i)

using namespace std;

template <class T>
T read(T x = 0) {
    char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9')
        x = x*10 + c-'0', c = getchar();
    return x;
}

vector<int> G[mx_n];
int n, m, Ans[mx_m], Cat[mx_n];

namespace aho_corasick {
    queue<int> Q;
    int tot, fail[mx_node];
    vector<int> val[mx_node];
    map<int, int> ch[mx_node];

    void Bfs_fail() {
        Q.push(0);
        while(!Q.empty()) {
            int u = Q.front(); Q.pop();
            for(map<int, int>::iterator it = ch[u].begin(); it != ch[u].end(); ++it) {
                int x = it -> first, y = it -> second;
                if(u) {
                    int t = fail[u];
                    for(; t && !ch[t][x]; t = fail[t]);
                    fail[y] = ch[t][x];
                }Q.push(y);
            }
        }
    }
}using namespace aho_corasick;

int s[mx_n], vis[mx_m];
void Query(int x) {
    int u = 0, top = 0;
    rep(i, 0, (int) G[x].size()-1) {
        int id = G[x][i];
        for(; u && !ch[u][id]; u = fail[u]);
        for(int v = (u=ch[u][id]); v; v = fail[v])
            rep(j, 0, (int)val[v].size()-1) if(!vis[val[v][j]]) {
                ++Cat[x], ++Ans[val[v][j]], vis[s[++top]=val[v][j]] = 1;
            }
    }rep(i, 1, top) vis[s[i]] = 0;
}

void input() {
    n = read<int>(), m = read<int>();
    rep(i, 1, n) rep(Case, 0, 1) {
        int len = read<int>();
        while(len--) G[i].pb(read<int>());
        G[i].pb(10002);
    }rep(i, 1, m) {
        int u = 0;
        int len = read<int>();
        while(len--) {
            int x = read<int>();
            if(!ch[u][x]) ch[u][x] = ++tot;
            u = ch[u][x];
        }val[u].pb(i);
    }Bfs_fail();
}

int main() {
    input();
    rep(i, 1, n) Query(i);
    rep(i, 1, m) printf("%d\n", Ans[i]);
    rep(i, 1, n) 
        printf("%d%c", Cat[i], i ^ n? ' ': '\n');
    return 0;
}
### 关于 SCOI2009 WINDY 数的解法 #### 定义与问题描述 WINDY数是指对于任意两个相邻位置上的数字,它们之间的差至少为\(2\)。给定正整数区间\([L, R]\),计算该范围内有多少个WINDY数。 #### 动态规划方法解析 为了高效解决这个问题,可以采用动态规划的方法来处理。定义状态`dp[i][j]`表示长度为`i`且最高位是`j`的WINDY数的数量[^3]。 - **初始化** 对于单个数字的情况(即只有一位),显然每一位都可以单独构成一个合法的WINDY数,因此有: ```cpp dp[1][d] = 1; // d ∈ {0, 1,...,9} ``` - **状态转移方程** 当考虑多位数时,如果当前位选择了某个特定数值,则下一位的选择会受到限制——它必须满足与前一位相差不小于2的要求。具体来说就是当上一高位为`pre`时,当前位置可选范围取决于`pre`的具体取值: - 如果`pre >= 2`, 则可以选择`{0... pre-2}` - 否则只能从剩余的有效集合中选取 这样就可以通过遍历所有可能的状态来进行状态间的转换并累加结果。 - **边界条件处理** 特殊情况下需要注意的是,在实际应用过程中还需要考虑到给出区间的上下限约束。可以通过逐位比较的方式判断是否越界,并据此调整有效状态空间大小。 ```cpp // 计算不超过num的最大windy数数量 int calc(int num){ int f[15], g[15]; memset(f, 0, sizeof(f)); string s = to_string(num); n = s.size(); for (char c : s) { a[++len] = c - '0'; } // 初始化f数组 for (int i=0;i<=9;++i)f[1][i]=1; // DP过程省略... return sum; } long long solve(long long L,long long R){ return calc(R)-calc(L-1); } ``` 此代码片段展示了如何利用预处理好的`dp`表快速查询指定范围内的WINDY数总量。其中`solve()`函数用于返回闭区间\[L,R\]内符合条件的总数;而辅助函数`calc()`负责根据传入参数构建相应的状态序列并最终得出答案。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值