1. 程式人生 > >網易遊戲互娛2018秋招筆試題-字元迷陣

網易遊戲互娛2018秋招筆試題-字元迷陣

1.題目描述

字元迷陣是一種經典的智力遊戲。玩家需要在給定的矩形的字元迷陣中尋找特定的單詞。
在這題的規則中,單詞是如下規定的:
1. 在字元迷陣中選取一個字元作為單詞的開頭;
2. 選取右方、下方、或右下45度方向作為單詞的延伸方向;
3. 以開頭的字元,以選定的延伸方向,把連續得到的若干字元拼接在一起,則稱為一個單詞。
這裡寫圖片描述
以圖1為例,如果要在其中尋找單詞”WORD”,則綠色框所標示的都是合法的方案,而紅色框所標示的都是不合法的方案。
現在的問題是,給出一個字元迷陣,及一個要尋找的單詞,問能在字元迷陣中找到多少個該單詞的合法方案。注意合法方案是可以重疊的,如圖1所示的字元迷陣,其中單詞”WORD”的合法方案有4種。

1.1輸入描述

輸入的第一行為一個正整數T,表示測試資料組數。 接下來有T組資料。每組資料的第一行包括兩個整數m和n,表示字元迷陣的行數和列數。接下來有m行,每一行為一個長度為n的字串,按順序表示每一行之中的字元。再接下來還有一行包括一個字串,表示要尋找的單詞。 資料範圍: 對於所有資料,都滿足1<=T<=9,且輸入的所有位於字元迷陣和單詞中的字元都為大寫字母。要尋找的單詞最短為2個字元,最長為9個字元。字元迷陣和行列數,最小為1,最多為99。 對於其中50%的資料檔案,字元迷陣的行列數更限制為最多為20。

1.2輸出描述

對於每一組資料,輸出一行,包含一個整數,為在給定的字元迷陣中找到給定的單詞的合法方案數。

1.3輸入例子1

3
10 10
AAAAAADROW
WORDBBBBBB
OCCCWCCCCC
RFFFFOFFFF
DHHHHHRHHH
ZWZVVVVDID
ZOZVXXDKIR
ZRZVXRXKIO
ZDZVOXXKIW
ZZZWXXXKIK
WORD
3 3
AAA
AAA
AAA
AA
5 8
WORDSWOR
ORDSWORD
RDSWORDS
DSWORDSW
SWORDSWO
SWORD

1.4輸出例子1

4
16
5

2.題目分析

對於該題,我並沒有什麼比較好的想法,可以說是用暴力解來做的。

2.1 題目分解

首先,在字元矩形尋找特定的單詞,分解一下題目,把題目分成三部分來做:
1. 對於第一種情況,m行個字串來說,計算每一個字串有多少個特定的單詞;
2. 對於第二種情況,n列個字串來說,計算每一個字串有多少個特定的單詞;
3. 對於第三種情況,若干個對角線字串來說,計算每一個字串有多少個特定的單詞。

2.2 題目推導

現在問題變成了:
1. 如何處理字元矩陣,將字元矩陣變成上述三種情況的字串陣列;
2. 對於每個字串,如何尋找到特定單詞的個數。

2.1 具體演算法

依次來做:
1. 將字元矩陣變成上述三種情況的字串陣列:
這個就是硬編碼能力了,無非就是橫著列印陣列,豎著列印陣列,以及斜著列印陣列的思想。
注意,在斜著的情況下,如果m,n中的最小值,小於單詞的長度,那個這種情況就不用考慮了。因為你會發現,斜著的字串最長的情況下,為m,n中的小值,根本不可能出現能匹配到單詞的情況。
2. 計算每一個字串有多少個特定的單詞:
我腦子裡面的第一反應就是KMP演算法,複雜度低,更關鍵的是,KMP演算法 返回的是匹配串的下標,對於字串中有多個單詞的情況,是非常有用的。
具體的做法是:

public static int processOneString(String s, String key) {
    int count = 0;
    int index = getIndexOf(s, key);
    //對於字串s存在單詞key的情況下
    if (index >= 0) {
        //計算s及其字串中單詞的數量
        for (int j = 0; j <= s.length() - key.length();) {
            index = getIndexOf(s.substring(j, s.length()), key);
            if (index >= 0) {
                //如果存在單詞,那麼下一次字串的起始位置是:
                //返回的下標+1
                j += (index + 1);
                count++;
            } else {
                j++;
            }
        }
    }
    //對於字串s存在單詞key的情況下,什麼也不做
    return count;
}

3.完整的AC程式碼

import java.util.*;

public class FindString {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int T = in.nextInt();
        in.nextLine();
        for (int k = 0; k < T; k++) {
            int m = in.nextInt();
            int n = in.nextInt();
            in.nextLine();
            String[] ss = new String[m];
            for (int i = 0; i < m; i++) {
                ss[i] = in.nextLine();
            }
            String key = in.nextLine();
            System.out.println(processRow(ss, key) + processCol(ss, key) + processDiagonal(ss, key));
        }
        in.close();
    }

    /**
     * 處理第一種情況:即計算每一行的字串中包含要尋找的關鍵字的個數
     * @param arr : 一個字串陣列
     * @param key : 要尋找的關鍵字
     * */
    public static int processRow(String[] arr, String key) {
        int res = 0;
        for (int i = 0; i < arr.length; i++) {
            res += processOneString(arr[i], key);
        }
        return res;
    }

    /**
     * 處理第二種情況:即計算每一列的字串中包含要尋找的關鍵字的個數
     * @param arr : 一個字串陣列
     * @param key : 要尋找的關鍵字
     * */
    public static int processCol(String[] arr, String key) {
        int res = 0;
        int m = arr.length;
        int n = arr[0].length();
        String[] tempArr = new String[n];
        //將行串轉換為列
        for (int col = 0; col < n; col++) {
            StringBuilder sb = new StringBuilder();
            for (int row = 0; row < m; row++) {
                sb.append(arr[row].charAt(col));
            }
            tempArr[col] = sb.toString();
        }

        for (int i = 0; i < tempArr.length; i++) {
            res += processOneString(tempArr[i], key);
        }

        return res;
    }

    /**
     * 處理第三種情況:即計算對角線的字串中包含要尋找的關鍵字的個數
     * @param arr : 一個字串陣列
     * @param key : 要尋找的關鍵字
     * */
    public static int processDiagonal(String[] arr, String key) {
        int res = 0;

        int m = arr.length;
        int n = arr[0].length();

        //求輸入矩陣行列的小值,如果min<key.length() 說明不可能有滿足條件的情況出現
        int min = m > n ? n : m;       
        if (min >= key.length()) {
            LinkedList<String> list = new LinkedList<>();
            //求對角線上的字串,對角線出發點從(0,n-key.length())的位置開始
            //因為長度小於key長度的字元都不符合要求
            for (int col = n - key.length(); col >= 0; col--) {
                StringBuilder sb = new StringBuilder();
                int i = 0;
                int j = col;
                //i,j有一個達到m、n值,迴圈停止,一個斜著的字串尋找完畢
                while (i < m && j < n) {
                    sb.append(arr[i++].charAt(j++));
                }
                list.add(sb.toString());

            }
            //求對角線上的字串,對角線出發點從(1,0)的位置開始,到(m - key.length(),0)位置結束
            for (int row = 1; row <= m - key.length(); row++) {
                StringBuilder sb = new StringBuilder();
                int i = row;
                int j = 0;
                while (i < m && j < n) {
                    sb.append(arr[i++].charAt(j++));
                }
                list.add(sb.toString());
            }

            for (String s : list) {
                res += processOneString(s, key);
            }
        }

        return res;
    }

    /**
     * 計算一個字串中有多少個key
     * 
     * @param s 
     *      :輸入的字串
     * @param key 
     *      :要匹配的字串
     */
    public static int processOneString(String s, String key) {
        int count = 0;
        int index = getIndexOf(s, key);
        if (index >= 0) {
            for (int j = 0; j <= s.length() - key.length();) {
                index = getIndexOf(s.substring(j, s.length()), key);
                if (index >= 0) {
                    j += (index + 1);
                    count++;
                } else {
                    j++;
                }
            }
        }
        return count;
    }

    /**
     * KMP演算法,輸出字串在原串中首次出現的下標,如果沒有字串返回-1
     * @param s 
     *       :被匹配的串
     * @param m
     *       :匹配串
     * */
    public static int getIndexOf(String s, String m) {
        if (s == null || m == null || s.length() < 1 || s.length() < m.length()) {
            return -1;
        }
        char[] ss = s.toCharArray();
        char[] ms = m.toCharArray();
        int si = 0;
        int mi = 0;
        int[] next = getNextArray(ms);
        while (si < ss.length && mi < ms.length) {
            if (ss[si] == ms[mi]) {
                si++;
                mi++;
            } else if (next[mi] == -1) {
                si++;
            } else {
                mi = next[mi];
            }
        }
        return mi == ms.length ? si - mi : -1;
    }

    /**
     * KMP演算法中,計算match字串的nextArr陣列
     * @param ms : match字串
     * */
    public static int[] getNextArray(char[] ms) {
        if (ms.length == 1) {
            return new int[] { -1 };
        }
        int[] next = new int[ms.length];
        next[0] = -1;
        next[1] = 0;
        int pos = 2;
        int cn = 0;
        while (pos < next.length) {
            if (ms[pos - 1] == ms[cn]) {
                next[pos++] = ++cn;
            } else if (cn > 0) {
                cn = next[cn];
            } else {
                next[pos++] = 0;
            }
        }
        return next;
    }

}