最新文章
这里会显示最新的几篇文章摘要。
记录生活,分享知识,与你一起成长。
这里会显示最新的几篇文章摘要。
人们在英文字典中查找某个单词的时候可能不知道该单词的完整拼法,而只知道该单词的一个错误的近似拼法,这时人们可能陷入困境,为了查找一个单词而浪费大量的时间。带有模糊查询功能的电子字典能够从一定程度上解决这一问题:用户只要输入一个字符串,电子字典就返回与该单词编辑距离最小的几个单词供用户选择。
字符串 \(a\) 与字符串 \(b\) 的编辑距离是指:允许对 \(a\) 或 \(b\) 串进行下列“编辑”操作,将 \(a\) 变为 \(b\) 或 \(b\) 变为 \(a\),最少“编辑”次数即为距离。
JSOI 团队正在开发一款电子字典,你需要帮助团队实现一个用于模糊查询功能的计数部件:对于一个待查询字符串,如果它是单词,则返回 \(-1\);如果它不是单词,则返回字典中有多少个单词与它的编辑距离为 \(1\)。
第一行包含两个正整数 \(N\) 和 \(M\)。
接下来的 \(N\) 行,每行一个字符串,第 \(i+1\) 行为单词 \(W_i\),单词长度在 \(1\) 至 \(20\) 之间。
再接下来 \(M\) 行,每行一个字符串,第 \(i+N+1\) 表示一个待查字符串 \(Q_i\)。待查字符串长度在 \(1\) 至 \(20\) 之间。\(W_i\) 和 \(Q_i\) 均由小写字母构成,文件中不包含多余空格。
输出应包括 \(M\) 行,第 \(i\) 行为一个整数 \(X_i\):
\(X_i = -1\) 表示 \(Q_i\) 为字典中的单词;
否则 \(X_i\) 表示与 \(Q_i\) 编辑距离为 \(1\) 的单词的个数。
4 3
abcd
abcde
aabc
abced
abcd
abc
abcdd
-1
2
3
abcd 在单词表中出现过;abc 与单词 abcd、aabc 的编辑距离都是 \(1\);abcdd 与单词 abcd、abcde、abced 的编辑距离都是 \(1\)。一部分单词存在字典树树中,查询一个字符串有几个单词可以通过增添,替换,删除一个字符的一个操作得到。
可以看作是对查询的字符串进行一个操作,查询字典树是否有这个单词。使用dfs进行搜索,首先需要考虑dfs的参数
plenthfvoid dfs(int p,int lenth,int f)操作:
dfs(p, lenth+1, 1)son[p][i]存在的情况下搜索下一个字符dfs(son[p][i], lenth+1, 1)dfs(son[p][i], lenth, 1)#include <bits/stdc++.h>
//#define int long long
using namespace std;
const int N = 2e5 + 10;
int n,m,son[N][26],cnt[N],idx,lens,vis[N],ans,flag;     //vis记录单词是否已经到达过,一样的单词不能算两个,别忘了每次查询清空!
char s[22];                                        
inline void ins(char s[]) {                             //插入字典树
    int len = strlen(s),p = 0;
    for (int i = 0;i < len;++i) {
        int t = s[i] - 'a';
        while (!son[p][t]) son[p][t] = ++idx;
        p = son[p][t];
    }
    cnt[p]++;                                          //标记单词末尾
}
void dfs(int p,int lenth,int f) {
    if ( lenth == lens && cnt[p] && !f) {             //对于直接得到的单词
        cout << -1 << '\n';
        flag = 1;
        return;
    }
    if (lenth == lens && cnt[p] && !vis[p]) {         //对于修改后得到的单词
        vis[p] = 1;
        ans++;
        return;
    }
    if (!f) {                                           //在没有改动过的前提下
        if (lenth < lens) dfs(p,lenth+1,1);          //删除一个字符,注意删除条件
        for (int i = 0;i < 26;++i) {
           if (son[p][i]) dfs(son[p][i],lenth,1);     //增添操作
            if (i != s[lenth]-'a' && son[p][i]) dfs(son[p][i],lenth+1,1); //替换操作,注意i对应是s[lenth] - 'a',感觉i != s[lenth] - 'a'没用,确实可以ac,但是如果替换自己会发生什么?相当于没换,浪费了修改机会,但这种情况会在其他情况出现。还是留着保险
        }
    }
    if (lenth == lens) return;                          //前面循环已经有过增添了,所以不用担心在末尾增添加成为一个单词的情况
    if (son[p][s[lenth] - 'a']) dfs(son[p][s[lenth] - 'a'],lenth+1,f);  //正常查询的情况
}
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> n >> m;
    while (n--) {
        cin >> s;
        ins(s);
    }
    while (m--) {
        memset(vis,0,sizeof(vis));    //对每次查询都要重置标记数组
        cin >> s;
        lens = strlen(s);     
        ans = 0,flag = 0;
        dfs(0,0,0);
        if (!flag) cout << ans << '\n';
    }
}
