[lnsyoj2232]永恒

题意

给定一个由小写字母和数字组成的字符串 s,给定 n 个模式串 t,对第 i 个字符串 ti,使得字符串 s 满足:删除其中任意多个字符,使 s 形式为 T + Date,其中,T 为模式串 tiDate 为任意一个真实存在的日期。求对于每一个 ti,求满足条件的 s 的个数的和

赛时 70PTS

由于我们要让 ti 尽可能靠前,Date 尽可能靠后,因此我们可以先扫描出 ti 的末尾位置 ed,然后枚举所有可能的日期(日期可以预处理),判断哪个日期存在于 ed 后的数字串中。

日期预处理

我们可以提前手打出每个月的天数,然后分别枚举月份和日期,并将月份和日期拼接起来,组合成一个 MMDD 形式的日期,最后存储起来。

赛后

参考赛时的思路,我们需要一种快速的方法查找出 ed 和判断是否有某个日期存在。
由于只需要找到位置就可以了,所以我们可以预处理出一个二维数组 nei,c,表示第 i 个字符后面的第一个字符 c 的位置,若不存在则为 1,特别地,ne0,c 表示第一个字符 c 的位置。
这个数组可以从后面开始递推:显然 nelen(s),c 的值一定为 1。当枚举到第 i 项时,显然,nei,si+1 的值为 i+1,而 nei,c(csi+1) 值为 nei+1,c,可以递推。
当我们查找 ed 时,我们就可以从 ne0,s1 开始向后查找,时间复杂度就被压缩到了 O(len(ti))
但我们无法在每一个 ti 中都进行一次合法日期的枚举,因此,我们仍需要预处理这一段。由赛时思路得,Date 应尽可能靠后。因此,我们提前枚举出所有合法日期的开头最靠后的位置,并记录下每个位置的合法日期的个数(每个日期只计算一次)。这样,只需要一个后缀和就可以 O(1) 的查询所有合法的 Date 了。
为了快速的枚举,我们需要另一个二维数组 prei,c,表示第 i 个字符前面的第一个字符 c 的位置,若不存在则为 1,特别地,nelen(s)+1,c 表示最后一个字符 c 的位置。由于定义是和 ne 完全相反的,因此预处理方式也与 ne 完全相反,这里不再赘述。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

const int N = 100005;

int days[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int T;
int n, m;
vector<string> dates;
string s, name;
int ne[N][128];
int pre[N][128];
int sum[N];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    for (int i = 1; i <= 12; i ++ ) 
        for (int j = 1; j <= days[i]; j ++ ){
            string date = "    ";
            date[0] = i / 10 + '0';
            date[1] = i % 10 + '0';
            date[2] = j / 10 + '0';
            date[3] = j % 10 + '0';
            dates.push_back(date);
        }

    cin >> T;

    while (T -- ){
        memset(sum, 0, sizeof sum);
        cin >> n >> m;
        cin >> s;
        s = ' ' + s;

        memset(ne[m], -1, sizeof ne[m]);
        for (int i = m - 1; i >= 0; i -- ){
            for (int j = 0; j < 128; j ++ )
                ne[i][j] = ne[i + 1][j];
            ne[i][s[i + 1]] = i + 1;
        }

        memset(pre[1], -1, sizeof pre[1]);
        for (int i = 2; i <= m + 1; i ++ ){
            for (int j = 0; j < 128; j ++ )
                pre[i][j] = pre[i - 1][j];
            pre[i][s[i - 1]] = i - 1;
        }

        for (string date : dates){
            int j = m + 1;
            for (int k = date.size() - 1; k >= 0 && j != -1; k -- ) j = pre[j][date[k]];
            if (j != -1) sum[j] ++ ;
        }
        for (int i = m; i; i -- ) sum[i] += sum[i + 1];
        int ans = 0;
        for (int i = 1; i <= n; i ++ ){
            cin >> name;
            int j = 0;
            for (int i = 0; i < name.size() && j != -1; i ++ ) j = ne[j][name[i]];
            ans += sum[j];
        }

        cout << ans << '\n';
    }
    return 0;
}
posted @   是一只小蒟蒻呀  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示