1. 程式人生 > >關於LeetCode中Ransom Note一題的理解

關於LeetCode中Ransom Note一題的理解

題目如下:


Given
 an 
arbitrary
 ransom
 note
 string 
and 
another 
string 
containing 
letters from
 all 
the 
magazines,
 write 
a 
function 
that 
will 
return 
true 
if 
the 
ransom 
 note 
can 
be 
constructed 
from 
the 
magazines ; 
otherwise, 
it 
will 
return 
false. 



Each 
letter
 in
 the
 magazine 
string 
can
 only 
be
 used 
once
 in
 your 
ransom
 note.

Note:
You may assume that both strings contain only lowercase letters.

canConstruct("a", "b") -> false
canConstruct("aa", "ab") -> false
canConstruct("aa", "aab") -> true

    好吧,我承認第一次看題乾的時候我都沒有弄清楚它要讓我們做什麼。題目“Ransom Note”翻譯成中文的意思是“勒索信”。一般的在影視劇中出現的勒索信上的文字好像大都是從報紙或者雜誌上剪下下來的,這沒準就是這道題目最初的靈感來源。題乾的意思是說給定兩個字串分別為ransomNote和magazine,我們要寫一個function來判斷ransomNote是否能由magazine字串中的字母組成,而且每個出現在magazine中的字母只能在ransomNote中出現一次。而且你可以假設你處理的這兩個小些字串只包含小寫字母。下面舉幾個例子:

(1)ransomNote為“a”,magazine為“b”,此時magazine中沒有字母“a”,所以要返回false;

(2)ransomNote為“aa”,magazine為“ab”,此時magazine中雖然有字母“a”,但是隻有一個“a”,而ransomNote需要兩個“a”,magazine中“a”的數量不夠,所以要返回false;

(3)ransomNote為“aa”,magazine為“aab”,此時magazine中包含ransomNote需要的兩個“a”,所以要返回true;

    好,到這裡按例又該分析一波思路了。如果我們要判斷ransomNote是否可以由magazine中的字母組成,我們只需要判斷在ransomNote中出現的每種字母,在magazine中是否有足夠數量的同種字母與之對應。也就是說,假如在ransomNote中字母“a”出現了兩次,那麼字母“a”在magazine中只要出現大於等於2次就肯定能滿足ransomNote的需要。那麼,為了記錄ransomNote和magazine中每種字母和其對應數量的關係,我們就會用到HashMap這種資料結構。將ransomNote和magazine中字母與出現次數的資訊分別存入兩個HashMap後,我們就可以通過比較兩個HashMap中key值對應的value值的大小來判斷ransomNote是否能由magazine構成。需要注意的是,比較使用的key值都是ransomNote對應的HashMap中的key值,這一點不要忘了。下面照例是已經Accepted的程式碼:

<span style="font-size:14px;">    public boolean canConstruct(String ransomNote, String magazine) {
         int ransomNoteLength = ransomNote.length();
        int magazineLength = magazine.length();
        if (magazineLength < ransomNoteLength){
            return false;
        }
        HashMap<String, Integer> ransomNoteMap = new HashMap<String, Integer>();
        HashMap<String, Integer> magazineMap = new HashMap<String, Integer>();
        ArrayList<String> temp = new ArrayList<String>();
        for (int i=0;i<ransomNoteLength;i++){
            String memeber = ransomNote.charAt(i)+"";
            if (!ransomNoteMap.containsKey(memeber)){
                ransomNoteMap.put(memeber,1);
                temp.add(memeber);
            }else{
                ransomNoteMap.put(memeber,ransomNoteMap.get(memeber)+1);
            }
        }

        for (int j=0;j<magazineLength;j++){
            String member2 = magazine.charAt(j)+"";
            if (!magazineMap.containsKey(member2)){
                magazineMap.put(member2,1);
            }else{
                magazineMap.put(member2,magazineMap.get(member2)+1);
            }
        }

        if (magazineMap.size()<ransomNoteMap.size()){
            return false;
        }else{
            for (String a:
                 temp) {
                if (magazineMap.get(a)==null || ransomNoteMap.get(a).intValue() > magazineMap.get(a).intValue() ){
                    return false;
                }
            }
        }

        return true;
    }</span>

    聽了這麼多廢話,下面是喜聞樂見的LeetCode討論區正片時間。正片中的方法在之前的部落格中也有介紹過,由於字串是由26個小寫英文字母組成的,所以我們可以宣告一個大小為26的int型別陣列,代表26個英文字母和它們出現的次數,其實這裡用“出現的次數”代表陣列中的數字是不太妥當的,陣列中數字的變化更像是生產者和消費者的關係,magazine是生產者,生產出一種字母就在其對應的位置上“加一”,ransomNote是消費者,每“消費”一個字母就在其對應的位置上“減一”。如果ransomNote能夠由magazine構成,那麼陣列所有的位置上肯定都是非負的,如果是負數說明ransomNote消費的,magazine根本生產不了那麼多,產能不足自然無法構成ransomNote。大致就是這麼一個道理,程式碼如下:

<span style="font-size:14px;">    public boolean canConstruct(String ransomNote, String magazine) {
        int[] arr = new int[26];
        for (int i = 0; i < magazine.length(); i++) {
            arr[magazine.charAt(i) - 'a']++;
        }
        for (int i = 0; i < ransomNote.length(); i++) {
            if(--arr[ransomNote.charAt(i)-'a'] < 0) {
                return false;
            }
        }
        return true;
    }</span>

   做多了我們就會發現其實LeetCode中有許多問題都是一個套路的,多學會一點套路就能多解決一點問題。