1. 程式人生 > >洛谷 P2580 於是他錯誤的點名開始了 hash/trie樹

洛谷 P2580 於是他錯誤的點名開始了 hash/trie樹

順序 ref for 如何 表示 www. bsp radius 詳情

題目背景

XS中學化學競賽組教練是一個酷愛爐石的人。

他會一邊搓爐石一邊點名以至於有一天他連續點到了某個同學兩次,然後正好被路過的校長發現了然後就是一頓歐拉歐拉歐拉(詳情請見已結束比賽CON900)。

題目描述

這之後校長任命你為特派探員,每天記錄他的點名。校長會提供化學競賽學生的人數和名單,而你需要告訴校長他有沒有點錯名。(為什麽不直接不讓他玩爐石。)

輸入輸出格式

輸入格式:

第一行一個整數 n,表示班上人數。接下來 n 行,每行一個字符串表示其名字(互不相同,且只含小寫字母,長度不超過 50)。第 n+2 行一個整數 m,表示教練報的名字。接下來 m 行,每行一個字符串表示教練報的名字(只含小寫字母,且長度不超過 50)。

輸出格式:

對於每個教練報的名字,輸出一行。如果該名字正確且是第一次出現,輸出“OK”,如果該名字錯誤,輸出“WRONG”,如果該名字正確但不是第一次出現,輸出“REPEAT”。(均不加引號)

輸入輸出樣例

輸入樣例#1: 復制
5  
a
b
c
ad
acd
3
a
a
e
輸出樣例#1: 復制
OK
REPEAT
WRONG

說明

對於 40%的數據,n≤1000,m≤2000;

對於 70%的數據,n≤10000,m≤20000;

對於 100%的數據, n≤10000,m≤100000。

第一眼就覺得是hash,寫完一丟上去,RE??,開大數組減小base再來一遍,RE??,再來一遍.......終於,在第七遍RE後,我發現事情並不簡單,下了個數據看看,明明自己機子上都能過,wtf??,被逼無奈下掏出trie樹

先介紹一手trie樹

trie樹,中文名字典樹(沒錯用英文只是為了裝逼),先看張圖

技術分享圖片

(以上來自百度)

除了根節點外(為什麽等下會說),字典樹中的每一個節點都代表一個字母,對任意一顆子樹進行遍歷都可以得到一個串,如上圖,從最左邊一路遍歷下去,依次經過a,d,d或a,m;可以得到add這個串或者am這個串,那如果我有一個ad的串該怎麽辦呢,這裏引入一個標記,例如有一個串ad,在構建字典樹時,我就對d這個節點打一個標記,表示從開頭遍歷到當前節點是一個串

對於一個trie[i][j]表示從i出發到i的第j棵子樹,那如何區分呢,我們規定一下兩個值

第一個,是進入trie的順序,此時相同字符的值可能不同

第二個,是節點子樹的值,此時相同字符的值一定相同(看不懂的話下面會結合代碼講)

介紹下trie的兩個基本操作:add和find

add

char ch[MAXN];//ch為待處理數組
void
add() { int len=strlen(ch),rt=0;//獲取長度,根節點為0 for(int i=0;i<len;i++) { int k=ch[i]-a;//第二個標記,k代表的是節點子樹對應的值 if(!trie[rt].son[k])//如果沒有這個節點 trie[rt].son[k]=++tot;//第一個標記,tot的值即為進入trie的順序 rt=trie[rt].son[k];//向下走 } trie[rt].have=1;//結束時打上一個標記,表示到目前是一個串 }

find

int find()
{
    int len=strlen(ch),rt=0;
    for(int i=0;i<len;i++)
       {
        int k=ch[i]-a; //前面的基本一樣   
        if(!trie[rt].son[k])return 3;//如果走不下去了,說明沒有這個串,返回3,表示沒有    
        rt=trie[rt].son[k]; //向下走       
            }
    if(!trie[rt].have)return 3;//如果沒有標記,說明沒有這個串,返回3,表示沒有
    if(!trie[rt].cnt)//如果第一次訪問
       {
        trie[rt].cnt++;
        return 1;//返回1,表示第一次出現    
            }
    return 2;否則返回2,表示出現過了        
}

完整代碼

#include<bits/stdc++.h>
using namespace std;
const int MAXN=10+1e6;
struct node{
    int son[27];
    bool have;
    int cnt;
}trie[MAXN];
int n,tot;
char ch[MAXN];
void add()
{
    int len=strlen(ch),rt=0;
    for(int i=0;i<len;i++)
       {
        int k=ch[i]-a;    
        if(!trie[rt].son[k])
           trie[rt].son[k]=++tot;    
        rt=trie[rt].son[k];    
            }
    trie[rt].have=1;        
}
int find()
{
    int len=strlen(ch),rt=0;
    for(int i=0;i<len;i++)
       {
        int k=ch[i]-a;    
        if(!trie[rt].son[k])return 3;    
        rt=trie[rt].son[k];        
            }
    if(!trie[rt].have)return 3;
    if(!trie[rt].cnt)
       {
        trie[rt].cnt++;
        return 1;    
            }
    return 2;        
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
       {
        scanf("%s",ch);    
        add();    
            }
    scanf("%d",&n);        
    for(int i=1;i<=n;i++)
       {
        scanf("%s",ch);    
        int k=find();
        if(k==1)
          cout<<"OK"<<endl;
        if(k==2)
          cout<<"REPEAT"<<endl;
        if(k==3)
          cout<<"WRONG"<<endl;    
            }
    return 0;
}

附贈蜜汁RE的hash

#include<bits/stdc++.h>
using namespace std;
const int MAXN=105;
struct node{
    int son[MAXN];
    bool have;
    int cnt;
}trie[MAXN];
int n,tot;
char ch[MAXN];
void add()
{
    int len=strlen(ch),rt=0;
    for(int i=0;i<len;i++)
       {
        int k=ch[i]-a;    
        if(!trie[rt].son[k])
           trie[rt].son[k]=++tot;    
        rt=trie[rt].son[k];    
            }
    trie[rt].have=1;        
}
int find()
{
    int len=strlen(ch),rt=0;
    for(int i=0;i<len;i++)
       {
        int k=ch[i]-a;    
        if(!trie[rt].son[k])return 3;    
        rt=trie[rt].son[k];        
            }
    if(!trie[rt].have)return 3;
    if(!trie[rt].cnt)
       {
        trie[rt].cnt++;
        return 1;    
            }
    return 2;        
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
       {
        scanf("%s",ch);    
        add();    
            }
    scanf("%d",&n);        
    for(int i=1;i<=n;i++)
       {
        scanf("%s",ch);    
        int k=find();
        if(k==1)
          cout<<"OK"<<endl;
        if(k==2)
          cout<<"REPEAT"<<endl;
        if(k==3)
          cout<<"WRONG"<<endl;    
            }
    return 0;
}

參考大佬@ Niko的題解

洛谷 P2580 於是他錯誤的點名開始了 hash/trie樹