/*
    N<=10000個文本串,M<=10000個要查詢的串
    對每個要查詢的串,有多少個文本串與該串的編輯距離<=1
    若編輯距離為0,輸出-1

    建立Trie樹,對于要查詢的串,狀態(tài)(root, p, change)
    表示當前在節(jié)點root,查詢的串檢查到p,change表示有沒修改過
    ------root及p之前的都匹配了,當前就是root的兒子跟p檢查了

    有匹配,添加,刪除,替換
    注意的是,查詢的是文本串的個數(shù),因為可能不同的變換,會導(dǎo)致到達同一個狀態(tài)
    -------需要判重,一方面加快速度,另一方面不會重復(fù)計數(shù)
    大數(shù)組判重,每次清空的話會很花時間,用cases數(shù)標記一下,表示在當前這個case
    已經(jīng)訪問過

    注意到達最后時,*p == 0 時,這個位置還可以再插入的,不要立即返回了

    hit 2888是升級版
    是求前綴,要注意重復(fù)計數(shù),還有清空問題等
*/

#include
<iostream>
#include
<cstring>
#include
<map>
#include
<algorithm>
#include
<stack>
#include
<queue>
#include
<cmath>
#include
<string>
#include
<cstdlib>
#include
<vector>
#include
<cstdio>
#include
<set>
#include
<list>
#include
<numeric>
#include
<cassert>
#include
<ctime>

using namespace std;

const int MAXN = 200010;

struct Trie {
    
int cnt;
    Trie 
*ch[26];
}
;

Trie nodes[MAXN], 
*ptr, *root;

char str[30];
int mark[MAXN][21][2], cases;

Trie
* newNode(){
    ptr
->cnt = 0;
    fill(ptr
->ch,  ptr->ch+26, (Trie*)NULL);//
    return ptr++;
}


void insert(Trie *root, char *p) {
    
if (*== 0{
        root
->cnt ++;
        
return ;
    }

    
if (root->ch[*p-'a'== NULL) {
        root
->ch[*p-'a'= newNode();
    }

    insert(root
->ch[*p-'a'] , p+1);
}



int gao(Trie *root, char *p , int change) {
    
if(mark[root-nodes][p-str][change>0== cases){
        
return 0;
    }

    mark[root
-nodes][p-str][change>0= cases;
    
if (*== 0{
        
int tot = root->cnt;
        
if!change ) {
            
if( root->cnt > 0 ) {
                
return -1;
            }

            
for ( int i = 0; i < 26; i++// add at last
                if( root->ch[i] != NULL) {
                    tot 
+= root->ch[i]->cnt;
                    mark[root
->ch[i]-nodes][p-str][1= cases;//note!!!!
                }

            }

        }

        
return tot;
    }


    
int tot = 0;
    
//match *p
    if (root->ch[*p-'a'!= NULL) {
        
int tmp = gao(root->ch[*p-'a'] , p+1, change);
        
if (tmp == -1{
            
return -1;
        }

        tot 
+= tmp;
    }

    
if(!change) {
        
//del
        tot += gao(root, p+11);
        
for (int i = 0; i < 26; i++{
            
if (root->ch[i] != NULL ) {
                
//add
                tot += gao(root->ch[i], p , 2);
                
if( i != *p-'a'){
                    
//replace
                    tot += gao(root->ch[i], p+1 , 4);
                }

            }

        }

    }

    
return tot;
}


int main()
{

#ifndef ONLINE_JUDGE
    freopen(
"in","r",stdin);
#endif

    
for ( int n, m; ~scanf("%d%d"&n, &m); ) {
        ptr 
= nodes;
        root 
= newNode();
        
for (int i = 0; i < n ; i++{
            scanf(
"%s", str);
            insert(root, str);
        }

        
for (int i = 0 ;i < m; i++{
            cases
++;
            scanf(
"%s", str);
            printf(
"%d\n", gao(root, str, 0));
        }

    }

    
return 0;
}



以下是hit 2888的核心部分
/*
    對于串str,經(jīng)過一些變換,
    if (*p == 0) 說明匹配了,這個根到這個節(jié)點就是一個合法的前綴了
    所以狀態(tài) (root, p, edth) 當前樹的節(jié)點root,串匹配到p(p之前的已經(jīng)和root及之前的匹配了),可用距離edth
    如果某個節(jié)點已經(jīng)mark為2,表明它被某種變換匹配了,就不用繼續(xù)搜了
    if (root->mark == 2) {
        return;
    }
    這里又不是求變換的方法數(shù),所以之前的變換已經(jīng)可行了,當前的這種變換不用搞下去了,直接return
    然后對走過的路徑mark為1,為了以后找到這些走過的節(jié)點,計算總數(shù),還有清空,避免走無用的位置
    
    注意某個節(jié)點mark為2,有可能它下面的某個節(jié)點也被mark為2,要注意只保留離根最近的那個
*/


void gao(Trie *root, char *p , int edth) {
    
    
if (root->mark == 2{
        
return;
    }

    root
->mark = 1;
    
if (*== 0{
        root
->mark = 2;
        
return ;
    }

    
//match *p
    if (root->ch[*p-'a'!= NULL) {
        gao(root
->ch[*p-'a'] , p+1, edth);
    }

    
if(edth > 0{
        
//del
        gao(root, p+1, edth - 1);
        
for (int i = 0; i < 26; i++{
            
if (root->ch[i] != NULL) {
                
//add
                gao(root->ch[i], p , edth - 1);
                
if( i != *p-'a'){
                    
//replace
                    gao(root->ch[i], p+1 , edth - 1);
                }

            }

        }

    }

    
return ;
}


int gao(Trie *root) {//cal and clear
    if (root -> mark == 0{
        
return 0;
    }

    
int ans = 0;
    
for (int i = 0; i < 26 ; i ++{//mark為2,也有可能下面的節(jié)點也有2,要繼續(xù)清空
        if (root->ch[i] != NULL) {
            ans 
+= gao(root->ch[i]);
        }

    }

    
if (root->mark == 2){//只保留離根最近的那個值
        ans = root->cnt;
    }

    root
->mark = 0;
    
return ans;
}