CSC1003 A2 T3 Ascii Art

Pizza1123發表於2024-10-30

A2 T3 Ascii Art

Tutorial by George (Wechat ID: Geo1123rge).

Keep it private before the assignment ends.

這裡用雜湊做法。

讀入字元模板

import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner S = new Scanner(System.in);
        int[] record = new int[26]; // 每個大字母的雜湊值
        for (int i = 0; i < 26; ++i) {
            String[] s = new String[7]; // 所有大字母高度都是 7
            int[] len = new int[7]; // 這裡不必要,但是因為 String 類的 .length() 複雜度是 O(length) 的,所以存一下快一點。還有另外一個作用就是輔助後面刪除字串尾空格
            int mx = 0; // 一定要求出字母每行的最大寬度,因為單個字母的最大寬度就是它在單詞裡的寬度
            for (int j = 0; j < 7; ++j) {
                s[j] = S.nextLine();
                while (s[j].length() < 2) s[j] = S.nextLine(); // 丟棄空行
                len[j] = s[j].length();
                while (len[j] > 0 && s[j].charAt(len[j] - 1) != '#') --len[j]; // 注意這很重要,字串尾可能會有空格
                if (len[j] > mx) mx = len[j];
            }
            int mod = 998244353; // 雜湊模數,具體作用看後面
            int sum = 0;
            for (int j = 0; j < 7; ++j) for (int k = 0; k < mx; ++k) { // 對整個 7 * mx 的方陣雜湊
                sum = sum * 2 % mod;
                if (k < len[j] && s[j].charAt(k) == '#') sum = (sum + 1) % mod;
            }
            // 仔細觀察上面兩行,如果不看 % mod 且 sum 不會爆 int,那麼求出來的 sum 就是把大字母矩陣 7 行接在一起後的二進位制數。但是這個數太大,所以對 998244353 取模——我們發現所有相同字母這樣計算後的值(可稱為雜湊值)都是一樣的,而不同字母都是不一樣的(這是個巧合,但是每個字母有 998244353 種雜湊值的可能取值,所以出現相同的機率真的很小,而這裡的機率就是 0,對比輸出的 record[i] 即可知)
            // System.out.println((char)(i + 'A') + ": " + sum);
            // for (int j = 0; j < 7; ++j) System.out.print(len[j] + " ");
            // System.out.println();
            record[i] = sum;
        }
        for (int i = 0; i < 26; ++i) System.out.print(record[i] + ", ");
        System.out.println();
    }
}

輸出結果:

557138, 783037474, 253713189, 246166578, 57958169, 57958106, 247060227, 838444457, 258369135, 355913267, 302150034, 8613417, 592770654, 528807205, 512275521, 782249380, 518568510, 785398183, 132274982, 567184348, 167355700, 117667438, 565824435, 495066258, 206529121, 921117289, 

解題程式碼

import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner S = new Scanner(System.in);
        int[] h = {557138, 783037474, 253713189, 246166578, 57958169, 57958106, 247060227, 838444457, 258369135, 355913267, 302150034, 8613417, 592770654, 528807205, 512275521, 782249380, 518568510, 785398183, 132274982, 567184348, 167355700, 117667438, 565824435, 495066258, 206529121, 921117289}; // 把上題的輸出存成陣列,h[i] 是第 i 個大字母的雜湊值(A - h[0], B - h[1], etc.)
        // about how h[] is created, look at H
        int n = S.nextInt();
        while (n-- > 0) {
            String[] s = new String[7];
            int[] len = new int[7];
            int mx = 0;
            for (int j = 0; j < 7; ++j) {
                s[j] = S.nextLine();
                while (s[j].length() < 2) s[j] = S.nextLine();
                len[j] = s[j].length();
                while (len[j] > 0 && s[j].charAt(len[j] - 1) != '#') --len[j];
                if (len[j] > mx) mx = len[j];
            } // 讀入一個單詞的字串陣列同前一份程式碼
            int mod = 998244353;
            int last = 0; // 見下,記錄上一個字母分隔後的下一列
            String res = "";
            for (int k = 0; k < mx; ++k) {
                boolean empty = true;
                for (int j = 0; j < 7; ++j) if (k < len[j] && s[j].charAt(k) == '#') {
                    empty = false;
                    break;
                } 
                // empty 表示列 k 是否為空,如果是空的,表示這裡有一個字母分隔
                if (empty) {
                    int sum = 0;
                    for (int j = 0; j < 7; ++j) for (int p = last; p < k; ++p) {
                        sum = sum * 2 % mod;
                        if (p < len[j] && s[j].charAt(p) == '#') sum = (sum + 1) % mod;
                    } // 同前一份程式碼,把兩個空列間的大字母矩陣展開雜湊
                    for (int x = 0; x < 26; ++x) if (h[x] == sum) { // 判斷是不是字母 (char)(x + 'A')
                        res += (char)(x + 'A');
                        break;
                    }
                    last = k + 1; // 記錄上一個字母分隔後的下一列
                }
            }
			// 下面:別忘了最後一個空列之後雖然已沒有空列,但是還有一個大字母矩陣,所以還要展開雜湊
            int sum = 0;
            for (int j = 0; j < 7; ++j) for (int p = last; p < mx; ++p) {
                sum = sum * 2 % mod;
                if (p < len[j] && s[j].charAt(p) == '#') sum = (sum + 1) % mod;
            }
            for (int x = 0; x < 26; ++x) if (h[x] == sum) {
                res += (char)(x + 'A');
                break;
            } // 同上
            System.out.println(res);
        }
    }
}

題目至此解決。雜湊的好處不關是可以減少程式碼長度,也可以讓我們對複雜的字母矩陣獲得更直觀的把握。在複雜的程式碼中一份程式碼讀入簡單的東西然後程式碼進行幾番複雜的計算之後可能又得到一個簡單的值,這個時候假如能在某個地方將程式碼打斷,將前半部分的成果總結為幾個簡單,甚至固定的資料,就再好不過了,能大大減少程式碼的複雜度,增加程式碼的正確性。

相關文章