这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战
题目
843. 猜猜这个单词
函数签名:
public void findSecretWord(String[] wordlist, Master master) {
}
分析
题目长的一笔,看看大概是个什么意思(设某个词为X,答案为Y):
-
答案在wordlist里。
-
调用master.guess(X),可以得知X有多少个位置上的字母和Y相同。
例如:Y = acckzz , X = abcczz , guess(X) = 4,自己mock代码如下:
class Master{ String word; public Master(String word) { this.word = word; } public int guess(String cmp){ int l = 6; int cnt = 0; for (int i = 0; i < l; i++) { if(word.charAt(i) == cmp.charAt(i)) cnt++; } return cnt; } } -
最多只能调用10次guess方法。
如何求解?划分到每一步试试。
假设某一次选取的单词为X,guess(X)= 3。
那么答案必然在和X有3次准确匹配的内容中,此时找出和X有3个准确匹配的集合为List[X]。
随后,从list[X]中取出和上一次的list[X]中有的数,逐步缩小可选的范围。
重复这个过程,看起来是可行的。
那么,为了快速地完成这个过程,就得提前做好和第i个字符有n次精确匹配的数组,方便我们在判断的时候不必反复计算。
public void findSecretWord(String[] wordlist, Master master) {
int n = wordlist.length;
//dp[i][j] 表示:wordlist的i和wordlist的j,有多少个位置上的单词相同
int[][] dp = new int[n][n];
//由于dp[i][j] 等于dp[j][i] ,因此只要计算一半,另一半map过去就可以了
for (int i = 0; i < wordlist.length; i++) {
for (int j = i+1;j < wordlist.length;j++){
dp[i][j] = matchLength(wordlist[i],wordlist[j]);
}
dp[i][i] = 6;
}
//map
for(int i = 0; i <wordlist.length; i++){
for(int j = 0; j< i; j++) dp[i][j] = dp[j][i];
}
//...
}
public int matchLength(String a,String b){
int cnt = 0;
for (int i = 0; i < 6; i++) {
if(a.charAt(i) == b.charAt(i)) cnt++;
}
return cnt;
}
这样子在后续的过程中,就可以通过这个dp数组,来快速判断和n相同的数有多少。
这一步也可以用map来做,但是这样子的话空间复杂度过大了一些。
剩下的代码就是更新可选取的内容了:
//上面的...补上这些代码
Set<Integer> candidates = null;
int curSelect = 0,times = 0;
while(true){
times++;
int cnt = master.guess(wordlist[curSelect]);
System.out.println("guess word:"+wordlist[curSelect]);
if(cnt==6) {
System.out.println(times);
return;
}
Set<Integer> cur = new HashSet<>();
int[] ints = dp[curSelect];
for (int i = 0; i < ints.length; i++) {
if (ints[i] == cnt && (null == candidates ||candidates.contains(i))) {
cur.add(i);
curSelect = i;
}
}
candidates = cur;
}
结果:
执行用时:3 ms, 在所有 Java 提交中击败了51.69%的用户
内存消耗:36.5 MB, 在所有 Java 提交中击败了57.63%的用户
优化
有没有可以改进的地方?
当然是有了,由于我们是提前做的数组初始化,由于只有10次,其实如果数组大小>10,那么必然有不会被访问到的数。那么,可以懒加载这个数组,只是在某些0的位置上需要提前做一点工作。