HDU 1880 題解(字符串哈希)

題面:

魔咒詞典

Time Limit: 8000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 14997 Accepted Submission(s): 3588

Problem Description
哈利波特在魔法學校的必修課之一就是學習魔咒。據說魔法世界有100000種不同的魔咒,哈利很難全部記住,但是爲了對抗強敵,他必須在危急時刻能夠調用任何一個需要的魔咒,所以他需要你的幫助。

給你一部魔咒詞典。當哈利聽到一個魔咒時,你的程序必須告訴他那個魔咒的功能;當哈利需要某個功能但不知道該用什麼魔咒時,你的程序要替他找到相應的魔咒。如果他要的魔咒不在詞典中,就輸出“what?”

Input
首先列出詞典中不超過100000條不同的魔咒詞條,每條格式爲:

[魔咒] 對應功能

其中“魔咒”和“對應功能”分別爲長度不超過20和80的字符串,字符串中保證不包含字符“[”和“]”,且“]”和後面的字符串之間有且僅有一個空格。詞典最後一行以“@END@”結束,這一行不屬於詞典中的詞條。
詞典之後的一行包含正整數N(<=1000),隨後是N個測試用例。每個測試用例佔一行,或者給出“[魔咒]”,或者給出“對應功能”。

Output
每個測試用例的輸出佔一行,輸出魔咒對應的功能,或者功能對應的魔咒。如果魔咒不在詞典中,就輸出“what?”

Sample Input
[expelliarmus] the disarming charm
[rictusempra] send a jet of silver light to hit the enemy
[tarantallegra] control the movement of one’s legs
[serpensortia] shoot a snake out of the end of one’s wand
[lumos] light the wand
[obliviate] the memory charm
[expecto patronum] send a Patronus to the dementors
[accio] the summoning charm
@END@
4
[lumos]
the summoning charm
[arha]
take me to the sky

Sample Output
light the wand
accio
what?
what?

分析:

此題其實就是字符串哈希的模板題,將字符串s1映射到s2,輸入s1求s2,輸入s2求s1
第一反應是用STL,但此題用map會MLE或TLE,所以手寫哈希表
字符串哈希的方法如下:
1.將前8位字母的ASCII碼乘積作爲key(用long long存),轉換爲數字的哈希問題
2.用數組和鏈表存儲,key對應的字符串存在數組下標爲key%c(c爲常數)的地方
若key%c相同,則存在鏈表裏,下圖直觀展示了該過程(圖中數字爲key)
這裏寫圖片描述
3.查找時先到key對應的數組,如果元素不對,則繼續遍歷鏈表,直到查到該元素

代碼實現:

先把字符串hash的代碼發上來

const int c=10000;
struct myhash{//評測機上hash是關鍵字
    struct node {
        long long key;//若key%c相同輔助判重
        string s1;//若key相同輔助判重
        string s2;//要查找的東西,字符串s1對應的字符串s2
        node* next;//鏈表用指針
    }a[c+5];
    void push(long long x,string s1,string s2) {//插入函數
        if(a[x%c].key==0) {//如果數組x%c處沒有元素,直接插入
            a[x%c].key=x;
            a[x%c].s1=s1;
            a[x%c].s2=s2;
        } else {//如果有,鏈表插入新節點(頭插入)
            node *tmp=new node();
            tmp->key=x;
            tmp->s1=s1;
            tmp->s2=s2;
            tmp->next=a[x%c].next;//此處相當於head
            a[x%c].next=tmp;
        }
    }
    string found(long long x,string s1) {//查找函數
        if(a[x%c].key==x&&a[x%c].s1==s1) return a[x%c].s2;//如果字符串在數組裏,直接返回對應的字符串s2
        if(a[x%c].key==0) return "what?";//如果數組裏沒有元素,鏈表裏肯定也沒有,返回what?
        else {
            node* tmp=a[x%c].next;//遍歷鏈表查找
            while(tmp!=NULL) {
                if(tmp->key==x&&tmp->s1==s1) return tmp->s2;
                tmp=tmp->next;
            }
            return "what?";
        }
    }
};

全部代碼如下,注意字符串處理:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int c=10000;
struct myhash{//評測機上hash是關鍵字
    struct node {
        long long key;//若key%c相同輔助判重
        string s1;//若key相同輔助判重
        string s2;//要查找的東西,字符串s1對應的字符串s2
        node* next;//鏈表用指針
    }a[c+5];
    void push(long long x,string s1,string s2) {//插入函數
        if(a[x%c].key==0) {//如果數組x%c處沒有元素,直接插入
            a[x%c].key=x;
            a[x%c].s1=s1;
            a[x%c].s2=s2;
        } else {//如果有,鏈表插入新節點(頭插入)
            node *tmp=new node();
            tmp->key=x;
            tmp->s1=s1;
            tmp->s2=s2;
            tmp->next=a[x%c].next;//此處相當於head
            a[x%c].next=tmp;
        }
    }
    string found(long long x,string s1) {//查找函數
        if(a[x%c].key==x&&a[x%c].s1==s1) return a[x%c].s2;//如果字符串在數組裏,直接返回對應的字符串s2
        if(a[x%c].key==0) return "what?";//如果數組裏沒有元素,鏈表裏肯定也沒有,返回what?
        else {
            node* tmp=a[x%c].next;//遍歷鏈表查找
            while(tmp!=NULL) {
                if(tmp->key==x&&tmp->s1==s1) return tmp->s2;
                tmp=tmp->next;
            }
            return "what?";
        }
    }
};
myhash H1,H2;//H1存儲名字對應的內容,H2存儲內容對應的名字
long long to_num(string str){//前8位的乘積hash 
    long long ans=1;
    int len=str.length();
    if(len>8) len=8;
    for(int i=0;i<len;i++) ans*=str[i];
    return ans;
}
int main() {
    int t; 
    string str,s1,s2;
    while(1) {
        int i;
        getline(cin,str);//有空格,必須用getline
        if(str=="@END@") break;
        for(i=0; i<str.length(); i++) {
            if(str[i]==']') break;//找到咒語名字和內容的分界點
        }
        s1=str.substr(0,i+1);
        s2=str.substr(i+2,str.length()-i-1);
        //將str分爲兩部分,s1是名字,s2是內容
        H1.push(to_num(s1),s1,s2);
        H2.push(to_num(s2),s2,s1);
    }
    cin>>t;
    getchar();//輸入一個換行符,否則換行符會被getline讀入到str中,導致在哈希表中查找'\n',多輸出一個"what?"
    while(t--){
        getline(cin,str);
        bool have_kuo=false;//區分輸入的是內容還是名字
        for(int i=0;i<str.length();i++){
            if(str[i]==']'){
                have_kuo=true;
                break;
            }
        } 
        string tmp;
        if(have_kuo) tmp=H1.found(to_num(str),str);
        else tmp=H2.found(to_num(str),str);
        if(tmp[0]=='['){
            for(int i=1;i<tmp.length()-1;i++)  cout<<tmp[i];//輸出咒語名字時不帶括號
            cout<<endl;
        }
        else cout<<tmp<<endl;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章