洛谷P4407 [JSOI2009]電子字典

海邊微風起發表於2019-06-19

題目描述

人們在英文字典中查詢某個單詞的時候可能不知道該單詞的完整拼法,而只知道該單詞的一個錯誤的近似拼法,這時人們可能陷入困境,為了查詢一個單詞而浪費大量的時間。帶有模糊查詢功能的電子字典能夠從一定程度上解決這一問題:使用者只要輸入一個字串,電子字典就返回與該單詞編輯距離最小的幾個單詞供使用者選擇。

字串a與字串b的編輯距離是指:允許對a或b串進行下列“編輯”操作,將a變為b或b變為a,最少“編輯”次數即為距離。

  1. 刪除串中某個位置的字母;
  2. 新增一個字母到串中某個位置;
  3. 替換串中某一位置的一個字母為另一個字母;

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;
}