網易遊戲互娛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;
}
}