[lnsyoj2232]永恒

题意

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

赛时 70PTS

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

日期预处理

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

赛后

参考赛时的思路,我们需要一种快速的方法查找出 \(ed\) 和判断是否有某个日期存在。
由于只需要找到位置就可以了,所以我们可以预处理出一个二维数组 \(ne_{i, c}\),表示第 \(i\) 个字符后面的第一个字符 \(c\) 的位置,若不存在则为 \(-1\),特别地,\(ne_{0,c}\) 表示第一个字符 \(c\) 的位置。
这个数组可以从后面开始递推:显然 \(ne_{len(s),c}\) 的值一定为 \(-1\)。当枚举到第 \(i\) 项时,显然,\(ne_{i,s_{i+1}}\) 的值为 \(i + 1\),而 \(ne_{i,c}(c\ne s_{i+1})\) 值为 \(ne_{i + 1,c}\),可以递推。
当我们查找 \(ed\) 时,我们就可以从 \(ne_{0,s_{1}}\) 开始向后查找,时间复杂度就被压缩到了 \(O(len(t_i))\)
但我们无法在每一个 \(t_i\) 中都进行一次合法日期的枚举,因此,我们仍需要预处理这一段。由赛时思路得,Date 应尽可能靠后。因此,我们提前枚举出所有合法日期的开头最靠后的位置,并记录下每个位置的合法日期的个数(每个日期只计算一次)。这样,只需要一个后缀和就可以 \(O(1)\) 的查询所有合法的 Date 了。
为了快速的枚举,我们需要另一个二维数组 \(pre_{i,c}\),表示第 \(i\) 个字符前面的第一个字符 \(c\) 的位置,若不存在则为 \(-1\),特别地,\(ne_{len(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 @ 2024-08-02 19:03  是一只小蒟蒻呀  阅读(27)  评论(0编辑  收藏  举报