题目大意:
口算难题
给你一个方程,左边用 words 表示,右边用 result 表示。
你需要根据以下规则检查方程是否可解:
- 每个字符都会被解码成一位数字(0 – 9)。
- 每对不同的字符必须映射到不同的数字。
- 每个 words[i] 和 result 都会被解码成一个没有前导零的数字。
- 左侧数字之和(words)等于右侧数字(result)。
如果方程可解,返回 True,否则返回 False。
示例 1:
输入:words = ["SEND","MORE"], result = "MONEY" 输出:true 解释:映射 'S'-> 9, 'E'->5, 'N'->6, 'D'->7, 'M'->1, 'O'->0, 'R'->8, 'Y'->'2' 所以 "SEND" + "MORE" = "MONEY" , 9567 + 1085 = 10652
示例 2:
输入:words = ["SIX","SEVEN","SEVEN"], result = "TWENTY" 输出:true 解释:映射 'S'-> 6, 'I'->5, 'X'->0, 'E'->8, 'V'->7, 'N'->2, 'T'->1, 'W'->'3', 'Y'->4 所以 "SIX" + "SEVEN" + "SEVEN" = "TWENTY" , 650 + 68782 + 68782 = 138214
示例 3:
输入:words = ["THIS","IS","TOO"], result = "FUNNY" 输出:true
示例 4:
输入:words = ["LEET","CODE"], result = "POINT" 输出:false
提示:
- 2 <= words.length <= 5
- 1 <= words[i].length, results.length <= 7
- words[i], result 只含有大写英文字母
- 表达式中使用的不同字符数最大为 10
如果想查看本题目是哪家公司的面试题,请参考以下免费链接: https://leetcode.jp/problemdetail.php?id=1307
解题思路分析:
本题是一道Hard级别的难题,看完题目之后,第一个想法就是暴力解,因为最多只有10种字符,我们分别用0-9表示每种字符,查看是否存在一种左侧数字之和等于右侧数字的方案即可。解题步骤大致如下:
- 先遍历题目给出的words数组以及result字符串,查看它们都含有哪些大写字母。我们使用一个长度为26的一位boolean型数组来记录出现过的字母,数组下标0-25分别对应字母a-z。如果存在该字母,数组对应下标的值更新为true。
- 使用递归方法为每一个字母赋值,赋值范围是0-9中的一个数字,递归函数的参数需要另一个boolean型数组来记录使用过的数字。递归的顺序为字母a到字母z。递归时,首先查看当前字母是否在words数组以及result字符串中出现过,如果没出现过,那么不需要赋值,直接递归到下一层子方法,查看下一个字母。反之如果出现过,那么我们则需要给当前字母赋值,我们循环0-9,使用每一个还没有使用过的数字来给当前字母赋值,赋值后,将当前数字标记为已经使用过,然后递归到下一层子问题为下一个字母赋值。这里需要注意一点,题目要求每个字符串的首字符不能以0开头,因此,如果当前字母是某个字符串的首字符时,不能赋值为0。
- 递归的结束条件为,当递归到字母z,并且赋值完成之后,说明所有字母都赋值成功,此时,将等式左边的每个单词以及等式右边的结果字符串,按照赋值的数值转化成数字,即可判断其是否相等。
实现代码:
boolean[] has=new boolean[26]; // 记录包含哪些字符
boolean[] firstChar = new boolean[26]; // 记录哪些字符是字符串的首字符
public boolean isSolvable(String[] words, String result) {
// 遍历每个单词
for(String w : words){
// 查看单词包含哪些字符
for(int i=0;i<w.length();i++){
int index=w.charAt(i)-'A';
has[index]=true;
if(i==0) firstChar[index]=true;
}
}
// 遍历result字符串每个字符,查看包含哪些字符
for(int i=0;i<result.length();i++){
int index=result.charAt(i)-'A';
has[index]=true;
if(i==0) firstChar[index]=true;
}
// 记录字符对应的数字(下标0-25代表字符A-Z)
int[] num = new int[26];
// 默认为-1
Arrays.fill(num, -1);
// 递归赋值
return dfs(words, result, num, 0, new boolean[10]);
}
// words为题目给出的等式左边所有字符串
// result为题目给出的等式右边字符串
// num为字符对应的数字
// letter为当前字符
// numUsed为使用过的数字
// 返回值为是否能够使等式相等
boolean dfs(String[] words, String result, int[] num, int letter, boolean[] numUsed){
if(letter==26){ // 26个字母都赋值完成时
int r=0; // 计算result对应的数字
for(int i=0;i<result.length();i++){
r= r*10+num[result.charAt(i)-'A'];
}
int sum=0; // 计算等式左边所有字符串对应数字之和
for(String w : words){
int n=0;
for(int i=0;i<w.length();i++){
n= n*10+num[w.charAt(i)-'A'];
}
sum+=n;
}
// 返回等式两边是否相等
return sum==r;
}
// 如果当前字母是题目给出的字符
if(has[letter]){
// 尝试从0-9循环赋值
for(int j=0;j<10;j++){
// 如果当前数字已经被使用过,跳过
if(numUsed[j]) continue;
// 如果当前数字是0,并当前字符是某个字符串的首字符,跳过
if(j==0&&firstChar[letter]) continue;
// 将当前字母赋值为当前数字
num[letter]=j;
// 记录当前数字已经使用过
numUsed[j]=true;
// 递归到下一个字母
if(dfs(words,result,num,letter+1,numUsed)) return true;
num[letter]=-1;
numUsed[j]=false;
}
}else{
// 如果当前字母不是题目给出的字符
// 递归到下一个字母
return dfs(words,result,num,letter+1,numUsed);
}
return false;
}本题解法执行时间为1337ms。
Runtime: 1371 ms, faster than 24.04% of Java online submissions for Verbal Arithmetic Puzzle.
Memory Usage: 34.7 MB, less than 100.00% of Java online submissions for Verbal Arithmetic Puzzle.
本网站文章均为原创内容,并可随意转载,但请标明本文链接如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: https://leetcode.jp/leetcode-1307-verbal-arithmetic-puzzle-解题思路分析/