1. 程式人生 > >(程式設計題)字母易位詞

(程式設計題)字母易位詞

題目

兩個單詞如果包含相同的字母,次序不同,則稱為字母易位詞(anagram)。例如,“silent”和“listen”是字母易位詞,而“apple”和“aplee”不是易位詞。請定義函式檢查兩個單詞是否是字母易位詞。可以假設兩個單詞字母均為小寫。要求演算法複雜度儘量低。

思路

  • 1.暴力破解:判斷str1中的每一個字母是否出現在str2中,同時我們需要一個大小與字串位數一致的陣列來記錄str2中已經被標記的位。
  public static boolean compare(String str1, String str2) {
    int i,j;
    if (str1 == null && str2 == null) {
      return true;
    }
    if ((str1 == null && str2 != null)
        || (str1 != null && str2 == null)
        || str1.length() != str2.length()) {
      return false;
    }
    // boolean 預設是false
    boolean[] flags = new boolean[str2.length()];
    for (i = 0; i < str1.length(); i++) {
      char temp = str1.charAt(i);
      for (j = 0; j < str2.length(); j++) {
        if (str2.charAt(j) == temp && !flags[j]) {
          flags[j]=true;
          break;
        }
      }
      if(j>=str2.length()){
        return false;
      }
    }
    return true;
  }

我們可以看到用到兩個for迴圈,一個會遍歷n次,裡層for迴圈我們假設最壞的情況是每次都走到最後一位(比如world和dlrow),那麼裡面的次數是n,n-1,n-2…1,複雜程度為

n(n-1)/2=1/2*n^2+1/2*n=O(n^2)
  • 2.是否可以先排序再一一比較就可以了,比較的時候最多是n次,那麼我們的排序演算法如果能降低複雜度的話,這也是一個好辦法。
  public static boolean compare2(String str1,String str2){

    if (str1 == null && str2 == null) {
      return true;
    }
    if ((str1 == null && str2 != null)
            || (str1 != null && str2 == null)
            || str1.length() != str2.length()) {
      return false;
    }
    char[] str1ToChar = str1.toCharArray();
    Arrays.sort(str1ToChar);
    char[] str2ToChar = str2.toCharArray();
    Arrays.sort(str2ToChar);
    for(int i=0;i<str1.length();i++){
      if(str1ToChar[i]!=str2ToChar[i]){
        return false;
      }
    }
    return true;
  }

我們知道java提供的排序演算法複雜度是O(n*logn),兩次排序加上比較:

2*O(n*logn)+n
  • 3.我們希望能進一步減低演算法複雜度,於是想到一個計數器,找到26大小的陣列來記錄每一個字母出現的次數,遍歷第一個陣列的時候,個數不斷增加,遍歷第二個陣列的時候,不斷減少,如果最後是0,就證明兩個字串中字元出現的次數都是一樣的。
  public static boolean compare(String str1,String str2){

    if (str1 == null && str2 == null) {
      return true;
    }
    if ((str1 == null && str2 != null)
            || (str1 != null && str2 == null)
            || str1.length() != str2.length()) {
      return false;
    }
    int[] num = new int[26];
    for(int i=0;i<str1.length();i++){
      num[str1.charAt(i)-'a']++;
    }
    for(int i=0;i<str2.length();i++){
      num[str2.charAt(i)-'a']--;
    }
    for(int i=0;i<num.length;i++){
      if(num[i]!=0){
        return false;
      }
    }
    return true;
  }

我們可以看到這個演算法中,遍歷兩次字串2*n,又遍歷一次m(m<=n).
所以演算法的複雜度是:

2*n+m=O(n)

總結:其實裡面沒有什麼深奧的演算法,只是一些小技巧,用空間來換取時間,一開始不比較,而是將字串的異同表現在字元出現的個數上,這樣子,問題就迎刃而解了。