匹配价值

匹配价值

给定一个字符串集合 SS 中包含 m 个长度为 n01 字符串,集合中可能包含重复元素。

给定一个长度为 n 的整数序列 w1,w2,,wn

关于两个长度为 n01 字符串 s,t 的匹配价值 V,其具体计算方法如下:

  • 设字符串 s 的各位字符从左到右依次为 s1,s2,,sn
  • 设字符串 t 的各位字符从左到右依次为 t1,t2,,tn
  • 初始时,V=0
  • 对于所有 i(1in),如果 si=ti,则 V 加上 wi
  • 最终得到的 V 即为两字符串的匹配价值。

现在,给定 q 个询问,每个询问包含一个长度为 n01 字符串 t 以及一个整数 k,具体询问内容为:请你计算并输出集合 S 中有多少个元素满足,与给定字符串 t 的匹配价值不大于 k

注意,如果集合中多个相同的元素均满足询问条件,则每个元素均应被计数。

输入格式

第一行包含三个整数 n,m,q

第二行包含 n 个整数 w1,w2,,wn

接下来 m 行,每行包含一个长度为 n01 字符串,表示集合 S 中的一个元素。

最后 q 行,每行包含一个长度为 n01 字符串 t 和一个整数 k,表示一个询问。

输出格式

每个询问输出一行答案,一个整数,表示满足询问条件的元素个数。

数据范围

3 个测试点满足 1m,q5
所有测试点满足 1n121m,q5×1050wi1000k100

输入样例1:

复制代码
2 4 5
40 20
01
01
10
11
00 20
00 40
11 20
11 40
11 60
复制代码

输出样例1:

2
4
2
3
4

输入样例2:

1 2 4
100
0
1
0 0
0 100
1 0
1 100

输出样例2:

1 1
2 2
3 1
4 2

 

解题思路

  这题思路是预处理。可以发现直接暴力的话肯定会超时,暴力枚举是为了在集合中统计与字符串匹配权值不超过k的个数,因此可以想到把枚举的过程进行预处理,查询的时候就可以控制到O(1)

  01字符串的最大长度为12,因此最多有212种不同的字符串,可以预处理两两配对的情况,一共有224种情况,这个计算量是可以过的,因此可以先预处理出这224种情况,然后算一下每种情况对应的权值是多少。同时在询问的时候权值是不超过100的,只有当计算的权值不超过100时才记录下来,因此开一个数组s[i,j]表示输入的字符串是i,权值为j时,集合中与i匹配的字符串个数。因为询问求的是权值不超过k的情况,因此预处理完后还需要对s数组求前缀和。

  在字符串进行匹配求权值时用位运算,就可以把这部分的计算量优化到O(1)。如果有01ab,为了求ab中有哪些位是相同的,可以先异或再对结果按位取反,即(ab),因为01串的位数不是固定32位,这个结果会使得高位全是1,因此事实上应该是ab((1<<n)1),其中n01串的位数。

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1 << 12, M = 110;
 5 
 6 int n, m, k;
 7 int w[12], mp[N];
 8 int cnt[N];
 9 int s[N][M];
10 
11 int get(char *str) {
12     int st = 0;
13     // 原字符串低位在前高位在后,转换后01串后低位在后高位在前,因此需要反过来处理
14     for (int i = n - 1; i >= 0; i--) {
15         st = st << 1 | str[i] - '0';
16     }
17     return st;
18 }
19 
20 int main() {
21     scanf("%d %d %d", &n, &m, &k);
22     
23     for (int i = 0; i < n; i++) {
24         scanf("%d", w + i);
25     }
26     for (int i = 0; i < 1 << n; i++) {  // 预处理2^n种情况对应的权值
27         for (int j = 0; j < n; j++) {
28             if (i >> j & 1) mp[i] += w[j];
29         }
30     }
31     
32     while (m--) {
33         char str[15];
34         scanf("%s", str);
35         cnt[get(str)]++;    // 统计集合中该字符串出现的次数
36     }
37     
38     for (int i = 0; i < 1 << n; i++) {      // i是询问的字符串
39         for (int j = 0; j < 1 << n; j++) {  // j是集合中的字符串
40             int t = i ^ j ^ (1 << n) - 1;   // 求i和j相同的位
41             if (mp[t] <= 100) s[i][mp[t]] += cnt[j];    // 如果权值不超过100就累加集合中j的次数(如果不存在j则为0)
42         }
43     }
44     
45     for (int i = 0; i < 1 << n; i++) {  // 求前缀和
46         for (int j = 1; j <= 100; j++) {
47             s[i][j] += s[i][j - 1];
48         }
49     }
50     
51     while (k--) {
52         char str[15];
53         int t;
54         scanf("%s %d", str, &t);
55         printf("%d\n", s[get(str)][t]);
56     }
57     
58     return 0;
59 }
复制代码

 

参考资料

  AcWing 4614. 匹配价值(AcWing杯 - 周赛):https://www.acwing.com/video/4317/  

posted @   onlyblues  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示