洛谷P4407 [JSOI2009]电子字典
题目描述
人们在英文字典中查找某个单词的时候可能不知道该单词的完整拼法,而只知道该单词的一个错误的近似拼法,这时人们可能陷入困境,为了查找一个单词而浪费大量的时间。带有模糊查询功能的电子字典能够从一定程度上解决这一问题:用户只要输入一个字符串,电子字典就返回与该单词编辑距离最小的几个单词供用户选择。
字符串a与字符串b的编辑距离是指:允许对a或b串进行下列“编辑”操作,将a变为b或b变为a,最少“编辑”次数即为距离。
- 删除串中某个位置的字母;
- 添加一个字母到串中某个位置;
- 替换串中某一位置的一个字母为另一个字母;
JSOI团队正在开发一款电子字典,你需要帮助团队实现一个用于模糊查询功能的计数部件:对于一个待查询字符串,如果它是单词,则返回-1;如果它不是单词,则返回字典中有多少个单词与它的编辑距离为1。
输入输出格式
输入格式:
第一行包含两个正整数N (N ≤ 10,000)和M (M ≤ 10,000)。
接下来的N行,每行一个字符串,第i + 1行为单词Wi。单词长度在1至20之间。
再接下来M行,每行一个字符串,第i + N + 1表示一个待查字符串Qi。待查字符串长度在1至20之间。Wi和Qi均由小写字母构成,文件中不包含多余空格。所有单词互不相同,但是查询字符串可能有重复。
输出格式:
输出应包括M行,第i行为一个整数Xi。Xi = -1表示Qi为字典中的单词;否则Xi表示与Qi编辑距离为1的单词的个数。
emmmmm, 据说是trie树, 不是很会呀, 勉强用hash搞一下吧; 首先存下每个单词的hash值, 排序是为了以后的查找。 依次读入每一个单词, 再用前缀和后缀和记录hash值,用二分查找是否存在, 存在即返回, 依次如题目所示枚举删除、添加、替换的点, 找到即ans++, 其中一些情况不必再次查找, 如类似aab的情况,删去两个a的效果是一样;最后输出即可;
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int INF = 0x3f3f3f3f; const int MAXN = 1e5 + 100; const int MAXM = 3e3 + 10; template < typename T > inline void read(T &x) { x = 0; T ff = 1, ch = getchar(); while(!isdigit(ch)) { if(ch == '-') ff = -1; ch = getchar(); } while(isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } x *= ff; } template < typename T > inline void write(T x) { if(x < 0) putchar('-'), x = -x; if(x > 9) write(x / 10); putchar(x % 10 + '0'); } int n, m; ull h[MAXN], p[25], s1[25], s2[25]; char s[25]; inline bool find(ull x) { int l = 1, r = n; while(l < r) { int mid = ((l + r) >> 1); if(h[mid] >= x) r = mid; else l = mid + 1; } return h[l] == x; } int main() { read(n); read(m); p[0] = 1; for(register int i = 1; i <= 21; ++i) p[i] = p[i - 1] * 131; for(register int i = 1; i <= n; ++i) { scanf("%s", s + 1); int len = strlen(s + 1); for(register int j = 1; j <= len; ++j) h[i] = h[i] * 131 + (s[j] - 'a' + 1); } sort(h + 1, h + n + 1); while(m--) { scanf("%s", s + 1); int len = strlen(s + 1), ans = 0; s2[len + 1] = 0; for(register int i = 1; i <= len; ++i) s1[i] = s1[i - 1] * 131 + (s[i] - 'a' + 1); if(find(s1[len])) { write(-1); putchar('\n'); continue; } for(register int i = len; i >= 1; --i) s2[i] = s2[i + 1] + (s[i] - 'a' + 1) * p[len - i]; for(register int i = 0; i < len; ++i) if(s[i] != s[i + 1]) if(find(s1[i] * p[len - i - 1] + s2[i + 2])) ++ans; //删除 for(register int i = 0; i <= len; ++i) for(register int j = 1; j <= 26; ++j) if(j != (s[i] - 'a' + 1)) if(find(s1[i] * p[len - i + 1] + j * p[len - i] + s2[i + 1])) ++ans; //添加 for(register int i = 1; i <= len; ++i) for(register int j = 1; j <= 26; ++j) if(j != (s[i] - 'a' + 1)) if(find(s1[len] + (j - (s[i] - 'a' + 1)) * p[len - i])) ++ans; //替换 printf("%d\n", ans); } return 0; }