题意
给定一个由小写字母和数字组成的字符串 \(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;
}
标签:int,sum,ne,日期,枚举,永恒,lnsyoj2232,31
From: https://www.cnblogs.com/XiaoJuRuoUP/p/-/lnsyoj2232