P4407 [JSOI2009] 电子字典 题解
题目:P4407
这题差不多就是 P1688 的改版。
参考一下我在 P1688 的做法,我们继续使用 Hash,然后只要考虑如何去重就好了。
于是就有了这个暴力的想法: #
代表修改, @
代表添加, ^
代表删除。
接下来我们来暴力去重。
考虑怎么样会使单词被重复统计。
- 在两个相邻且相同的字母中选一个删掉
- 在一个字母旁添加一个一模一样的字母
- 修改的字符不会被重复统计
然后我们就可以愉快去重了。
对于上面的 1、2 两种情况,考虑只统计 连续相同字母中的最后一个字母。
具体操作:
-
删除
(1). 插入字典时: 一位一位枚举,遇到连续相同字母时只改最后一位并塞入 Hash。
(2). 查询时: 所有空位全部插入^
并在 Hash 中查询。 -
添加
(1). 插入字典时: 所有空位全部插入@
并塞入 Hash 中。
(2). 查询时: 一位一位枚举,遇到连续相同字母时只改最后一位并在 Hash 中查询。 -
修改
照常枚举。
为什么要这样分类操作?
我们会发现只有在 1(1) 和 2(2) 情况下我们才能确切地知道当前字符串中枚举到的是不是重复字母,而另外两种情况是我们插入的,可以代表任意字符,只插一位会漏掉很多不同字母的情况。
这样操作就可以把重复的筛掉,且不会过多地去掉合法情况,暴力且能 A。
还有一种答案为 的情况,在一开始把所有字符串塞进 Hash,询问时先在 Hash 里找一找就好了。
Naive Code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll Min(ll x, ll y){return x < y ? x : y;}
inline ll Max(ll x, ll y){return x > y ? x : y;}
inline ll rd(){
ll x = 0;bool f = true;char c = getchar();
while (c < '0' || c > '9'){if (c == '-') f = false; c = getchar();}
while (c >= '0' && c <= '9'){x = (x<<3) + (x<<1) + (c ^ 48);c = getchar();}
return f ? x : -x;
}
inline string srd(){
char c = getchar();string res = "";
while (c < 'a' || c > 'z')c = getchar();
while(c >= 'a' && c <= 'z')res += c, c = getchar();
return res;
}
const int N = 1e4 + 10, M = 1e6 + 7;
class Hash_table{
private:
struct Node{
int nextt, num;// num就是该状态下的单词个数
string str;
}data[N * 60];int head[M + 10], cnt;
int h(string s){
int l = s.size();
int res = 0;
for(int i = 0; i < l; ++i)
res = ((res << 3) + (s[i] - 'a') + M) % M;
return res;
}
public:
int find(string s){
int key = h(s);
for(int i = head[key]; i; i = data[i].nextt)
if(s == data[i].str)
return data[i].num;
return 0;
}
void insert(string s){
int key = h(s);
for(int i = head[key]; i; i = data[i].nextt){
if(s == data[i].str){
data[i].num++;
return ;
}
}
data[++cnt].nextt = head[key];
data[cnt].num = 1;
data[cnt].str = s;
head[key] = cnt;
return ;
}
}H;
int n, m;
string s, tmp, tmp1, tmp2;
int main(){
n = rd(), m = rd();
for(int i = 1; i <= n; ++i){
s = srd();H.insert(s);
int l = s.size();
tmp = s;
for(int j = 0; j < l; ++j){
tmp[j] = '#';
H.insert(tmp);
if(j == l - 1 || s[j] != s[j + 1]){// 只删除最后一个
tmp[j] = '^';
H.insert(tmp);
}
tmp[j] = s[j];
}
if(l < 20){
for(int j = 0; j <= l; ++j){
tmp = tmp1 = tmp2 = "";
for(int k = 1; k <= j; ++k)
tmp1 += s[k - 1];
for(int k = j + 1; k <= l; ++k)
tmp2 += s[k - 1];
tmp += tmp1, tmp += '@', tmp += tmp2;
H.insert(tmp);
}
}
}
for(int i = 1; i <= m; ++i){
s = srd();
if(H.find(s)){
printf("-1\n");
continue;
}
int l = s.size(), ans = 0;
tmp = s;
for(int j = 0; j < l; ++j){
tmp[j] = '#';
ans += H.find(tmp);
if(j == l - 1 || s[j] != s[j + 1]){// 只添加最后一个
tmp[j] = '@';
ans += H.find(tmp);
}
tmp[j] = s[j];
}
if(l < 20){
for(int j = 0; j <= l; ++j){
tmp = tmp1 = tmp2 = "";
for(int k = 1; k <= j; ++k)
tmp1 += s[k - 1];
for(int k = j + 1; k <= l; ++k)
tmp2 += s[k - 1];
tmp += tmp1, tmp += '^',tmp += tmp2;
ans += H.find(tmp);
}
}
printf("%d\n", ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!