题目描述:给了n个字符串(仅含小写字母),m组查询,每组查询由k个8位数字组成,这8位数字组成8位二进制数代表一个字符(大写字母或小写字母)(输入的数值有5%的误差,原本这8个数只有a和2a,a代表0,2a代表1以此组成二进制串),如10.5 20.1 10.1 10.2 9.9 9.7 10.0 19.9去掉误差应该是10 20 10 10 10 10 10 20代表二进制01000001即十进制数65,这是A的ASCLL值,所以这8个数字代表A。求输入的这m组查询一共是多少字符串的前缀子串。至于误差处理我们想的是5%的误差绝对不是是它的1.5倍,所以如果一个数是它的1.5倍以上,则正确数值一定是它的2倍,相反如果小于1.5一定是它本身。而且所对应8位二进制最高位一定是0,否则它代表的不会是字母。
思路:场上是暴力过的,对于输入的每个字符串,先求出其所有的前缀子串,用map保存并记录其总共出现的次数。对于输入的m组查询求出其代表的字符串后直接在map中查找这个串出现的次数即可。赛后EF问我是用字典树写的吗,我说不是啊直接暴的,还被小小调侃了一下,场上能A就好嘛,干嘛笑话我不会字典树。赛后看了一下字典树,很好理解也确实挺好用的,所以又用字典树写了一遍。这样看来,这道题其实是一道很裸的字典树模板题。
两个版本代码如下:
暴力预处理记录所有子串及出现次数查询时直接找即可:
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<iostream>
#include<string>
#include<algorithm>
#include<map>
using namespace std;
const int maxn = 100000 + 66;
const double eps = 1e-6;
int n, m, k;
double a[10];
int num[10];
map<string, int> mp;
char changes(){
int cnt = 0;
for(int i = 0; i < 8; ++i){
cnt = cnt * 2 + num[i];
}
char ch = cnt;
return ch;
}
int main(){
while(~scanf("%d%d", &n, &m)){
mp.clear();
string str, tmp;
int len;
for(int i = 0; i < n; ++i){
cin >> str;
len = str.size();
for(int l = 1; l <= len; ++l){
tmp = str.substr(0, l);
++mp[tmp];
}
}
// for(map<string, int> :: iterator it = mp.begin(); it != mp.end(); ++it){
// cout << (*it).first << " " << (*it).second << endl;
// }
int ans = 0;
while(m--){
scanf("%d", &k);
string tmp = "";
char ch;
bool ok = true;
while(k--){
scanf("%lf", &a[0]);
num[0] = 0;
for(int i = 1; i < 8; ++i){
scanf("%lf", &a[i]);
if(a[i] / a[0] > 1.5 - eps) num[i] = 1;
else num[i] = 0;
}
if(!ok) continue;
ch = changes();
if(!(ch >= 'a' && ch <= 'z')){
ok = false;
}
tmp += ch;
}
if(!ok) continue;
ans += mp[tmp];
}
printf("%d\n", ans);
}
return 0;
}
Trie模板版本:
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
const int maxn = 100000 + 66;
const double eps = 1e-6;
int n, m, k;
double a[10];
int num[10];
struct node{
int value;
struct node *son[26];
node(){
value = 0;
memset(son, 0, sizeof son);
}
}*root;
void build(node *root, string str){
int len = str.size();
int x;
for(int i = 0; i < len; ++i){
x = str[i] - 'a';
if(root->son[x] == NULL){
root->son[x] = new node();
}
root = root->son[x];
++root->value;
}
}
int searchs(node *root, string str){
int len = str.size();
int x;
for(int i = 0; i < len; ++i){
x = str[i] - 'a';
if(root->son[x] == NULL) return 0;
root = root->son[x];
}
return root->value;
}
char changes(){
int x = 0;
for(int i = 0; i < 8; ++i){
x = x * 2 + num[i];
}
return char(x);
}
int main(){
while(~scanf("%d%d", &n, &m)){
root = NULL;
root = new node();
string str;
for(int i = 0; i < n; ++i){
cin >> str;
build(root, str);
}
bool ok;
char ch;
int ans = 0;
while(m--){
scanf("%d",&k);
str = "";
ok = true;
while(k--){
num[0] = 0; scanf("%lf", &a[0]);
for(int i = 1; i < 8; ++i){
scanf("%lf", &a[i]);
if(a[i] / a[0] > 1.5 + eps) num[i] = 1;
else num[i] = 0;
}
ch = changes();
if(!(ch >= 'a' && ch <= 'z')) ok = false;
str += ch;
}
if(!ok) continue;
ans += searchs(root, str);
}
printf("%d\n", ans);
}
return 0;
}