题目说明

所谓字母算式,就是用字母表示的算式,
规则是相同字母对应相同数字,不同字母对应不同数字,
并且第一位字母的对应数字不能是 0。

譬如给定算式 We * love = CodeIQ,则可以对应填上下面这些数字以使之成立。
W = 7, e = 4, l = 3, o = 8, v = 0, C = 2, d = 1, I = 9, Q = 6
这样一来,我们就能得到 74 * 3804 = 281496 这样的等式。
使前面那个字母算式成立的解法只有这一种。

求使下面这个字母算式成立的解法有多少种?
READ + WRITE + TALK = SKILL

思路

1.根据字母算式拆分出不重复的字母作为可选字母,0~9这十个数字作为可选数字
2.用两个集合分别保存可选字母和可选数字
3.用递归依次将每个字母替换成数字,将字母算式=>数字算式
4.判断算式是否成立

代码1

public static int count = 0; // 统计最终结果有多少个
public static void main(String[] args) {// 字母算式String pattern = "READ + WRITE + TALK = SKILL";pattern = pattern.replace(" ", ""); // 去除空格String str = pattern.replaceAll("[^a-zA-Z]", ""); // 去除+和=System.out.println(str); // READWRITETALKSKILL// 可用字符List<String> sList = new LinkedList<>();for (int i = 0; i < str.length(); i++) {String s = str.charAt(i) + "";if (!sList.contains(s)) { // 去重添加sList.add(s);}}System.out.println(sList); // [R, E, A, D, W, I, T, L, K, S]// 可用数字List<Integer> nList = new LinkedList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));calc(pattern, sList, nList);System.out.println("count = " + count); // 输出最终结果
}
/*** 每层递归替换一种字母,替换到最后一层时得到纯数字算式,计算算式是否成立* @param pattern   当前的字母算式(逐步被替换成数字算式)* @param sList     可选字母* @param nList     可选数字*/
private static void calc(String pattern, List<String> sList, List<Integer> nList) {if (pattern == null || sList == null || nList == null) throw new IllegalArgumentException("入参不合法!");// 字符用完啦,字母算式完全替换成数字算式了。开始判断算式是否成立if (sList.size() == 0) {String[] ss = pattern.split("[^0-9]"); // 按照+和=切分算式,得到4个数字// 此处的逻辑与字母算式耦合int a = Integer.parseInt(ss[0]);int b = Integer.parseInt(ss[1]);int c = Integer.parseInt(ss[2]);int d = Integer.parseInt(ss[3]);if (a + b + c == d) {// 打印出来瞅瞅成功的算式是哪些System.out.println(a + " + " + b + " + " + c + " = " + d); count ++;}return;}String s = sList.remove(0); // 取出当前集合中的第一个字母,并移除for (int j = 0; j < nList.size(); j++) {int n = nList.get(j); // 获取位置j上的数字if (n == 0 && "RWTS".contains(s)) { // 首位不为0continue;}nList.remove(j); // 移除位置j上的数字calc(pattern.replace(s, n + ""), sList, nList);nList.add(j, n); // 还原位置j上的数字}sList.add(0, s); // 还原第一个字母
}

代码2

代码1中的部分逻辑与字母算式严重耦合,意味着更换字母算式后需要修改多处代码。
以下代码的可复用性更好,直接更换为想要的字母算式即可。
鉴于Java中没有ruby那样的eval方法,所以需要自己实现相关代码。

main方法

public static int count = 0; // 统计最终结果有多少个
public static void main(String[] args) {// 字母算式String pattern = "READ + WRITE + TALK = SKILL";pattern = pattern.replace(" ", ""); // 去除空格String str = pattern.replaceAll("[^a-zA-Z]", ""); // 去除+和=System.out.println(str); // READWRITETALKSKILL// 首字母String[] ss = pattern.split("[^a-zA-Z]"); // 切分出每个单词String head = "";for(String s : ss){head += s.charAt(0); // 取每个单词的首字母}System.out.println(head); // RWTS// 可用字符List<String> sList = new LinkedList<>();for (int i = 0; i < str.length(); i++) {String s = str.charAt(i) + "";if (!sList.contains(s)) { // 去重添加sList.add(s);}}System.out.println(sList); // [R, E, A, D, W, I, T, L, K, S]// 可用数字List<Integer> nList = new LinkedList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));calc(pattern, sList, nList, head);System.out.println("count = " + count); // 输出最终结果
}

递归方法

/*** 每层递归替换一种字母,替换到最后一层时得到算式,计算算式是否成立* @param pattern   当前的字母算式(逐步被替换成数字算式)* @param sList     可选字母* @param nList     可选数字*/
private static void calc(String pattern, List<String> sList, List<Integer> nList, String head) {if (pattern == null || sList == null || nList == null) throw new IllegalArgumentException("入参不合法!");// 字符用完啦,字母算式完全替换成数字算式了。开始判断算式是否成立if (sList.size() == 0) {String[] ss = pattern.split("="); // 通过等号将算式切分成两部分if(Double.compare(CalcUtil.evaluate(ss[0]),Integer.parseInt(ss[1])) == 0){ // CalcUtil.evaluate是自定义的方法System.out.println(pattern);count ++;}return;}String s = sList.remove(0); // 取出当前集合中的第一个字母,并移除for (int j = 0; j < nList.size(); j++) {int n = nList.get(j); // 获取位置j上的数字if(n == 0 && head.contains(s)) { // 首字母不可为0continue;}nList.remove(j); // 移除位置j上的数字calc(pattern.replace(s, n + ""), sList, nList, head);nList.add(j, n); // 还原位置j上的数字}sList.add(0, s); // 还原第一个字母
}

CalcUtil.java(首次用于:《程序员的算法趣题:Q02 数列的四则运算(Java版)》)

package com.lanying.Q13有多少种满足字母算式的解法;import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import static java.lang.Double.NaN;public class CalcUtil {// 按照四则运算优先级,计算表达式的值,并返回public static Double evaluate(String exp) {/* 1.中缀表达式 --> 后缀表达式1) 通过正则表达式切分算数表达式2) 从左往右遍历中缀表达式的每个元素:数字和运算符3) 若是数字,直接存入List4) 若是符号,则①判断优先级②出栈③入栈:i. 判断当前符号与栈顶符号(最近一次存入的符号)的优先级ii. 如果该符号是右括号,或者优先级低于栈顶符号,则栈顶元素依次出栈并存入StringBuilderiii. 然后将当前符号入栈5) 遍历结束后,出栈所有运算符*/Stack<Double> nums = new Stack<>(); // 存放数字Stack<String> op = new Stack<>(); // 存放符号List<String> list = new LinkedList<String>(); // 保存数字和+-*/符号// 拆分出数字和操作符// \d+(\.\d+)?  表示数字部分,小括号里的是小数部分,?表示0~1次// (?<!\d)-?    表示数字部分前面的负号;?表示负号可以没有,或者有一个;(?<!\d)表示不能以数字开头,即这个负号的前面不能是数字(否则-就是减号,而不是负号了)Pattern p = Pattern.compile("(?<!\\d)-?\\d+(\\.\\d+)?|[+\\-*/()]"); // Java中的正则表达式用\\表示转义字符Matcher m = p.matcher(exp);while (m.find()) {String s = m.group(); // 依次找出每个元素(数字or字符)// 操作符if (s.matches("[+\\-*/()]")) {switch (s) {case "(":  // "(" 入栈op.push("(");break;case ")":  // ")" 出栈,直到"("String top = null; // 栈顶的运算符while (!(top = op.pop()).equals("(")) {list.add(top);}break;default:  // 四则运算符,比较优先级,出栈比当前符号优先级高的符号,然后入栈当前符号while (!op.empty() && opPriority(s) <= opPriority(op.peek())) { // 自定义的方法opPriority(xx)list.add(op.pop()); // 出栈}op.push(s); // 入栈当前运算符break;}} else { // 数字list.add(s);}}// 出栈所有运算符while (!op.isEmpty()) {list.add(op.pop());}//------- 至此,后缀表达式的每个部分均已存入list ------// 2.计算后缀表达式的值Double res = NaN; // not a number; 需要导包import static java.lang.Double.NaN;for (String e : list) { // 依次取出list中的每个操作数和运算符if (e.matches("[+\\-*/()]")) { // +-*/运算符,取出nums栈顶的两个数字进行运算,然后将结果存入numsdouble next = nums.pop();double pre = nums.pop();res = calc(pre, next, e); // 自定义的方法calc(xx)nums.push(res);} else { // 数字,直接存入numsnums.push(Double.parseDouble(e));}}return res;}// 四则运算private static double calc(double pre, double next, String op) {switch (op) {case "+":return pre + next;case "-":return pre - next;case "*":return pre * next;case "/":return pre / next; // Java中两个double相除,0为除数不会报错default:break;}throw new IllegalArgumentException("不支持的操作符!");}// 计算运算符的优先级。此处指定:数字越大,优先级越高private static int opPriority(String op) {if (op == null) return 0;switch (op) {case "(":return 1;case "+":case "-":return 2;case "*":case "/":return 3;default:throw new IllegalArgumentException("不支持的操作符!");}}
}

结果

1632 + 41976 + 7380 = 50988
2543 + 72065 + 6491 = 81099
4905 + 24689 + 8017 = 37611
5094 + 75310 + 1962 = 82366
5096 + 35710 + 1982 = 42788
5180 + 65921 + 2843 = 73944
5270 + 85132 + 3764 = 94166
7092 + 37510 + 1986 = 46588
7092 + 47310 + 1986 = 56388
9728 + 19467 + 6205 = 35400
count = 10

程序员的算法趣题:Q13 有多少种满足字母算式的解法(Java版)相关推荐

  1. 程序员的算法趣题:Q22 不缠绕的纸杯电话(Java版)

    题目说明 用绳子连接纸杯制作"纸杯电话"--这应该勾起了很多人对理科实验的回忆. 如果把绳子拉直,对着一边的纸杯讲话,声音就可以从另一边的纸杯传出. 假设有几个小朋友以相同间隔围成 ...

  2. 程序员的算法趣题:Q29 合成电阻的黄金分割比(Java版)

    题目说明 我们在物理课上都学过"电阻",通过把电阻串联或者并联可以使电阻值变大或者变小. 电阻值分别为 R1.R2.R3的 3 个电阻串联后,合成电阻的值为R1+R2+R3. 同样 ...

  3. 程序员的算法趣题Q13: 满足字母算式的解法

    目录 1. 问题描述 2. 解题思路 3. 代码及测试 4. 优化 1. 问题描述 所谓字母算式,就是用字母表示的算式,规则是相同字母对应相同数字,不同字母对应不同数字,并且第一位字母的对应数字不能是 ...

  4. 程序员的算法趣题Q29: 合成电阻的黄金分割比

    目录 1. 问题描述 2. 解题分析 2.1 分割成两组 2.2 N=5时的分割例 2.3 阻值计算例 2.4 算法实现流程 3. 代码及测试 4. 后记 4.1 分割的洞见 4.2 另一种思路 1. ...

  5. 《程序员的算法趣题》-(日)增井敏克 Python解题 -- (Q13)

    <程序员的算法趣题>-(日)增井敏克 , 书中为69 道数学谜题编写了解题程序, 编程语言为:Ruby,JavaScript,C语言.有兴趣的同学,可以购书阅读~ 在此更新个人编写的Pyt ...

  6. php算法求出一个数可以被分解成多少个_程序员的算法趣题

    计算机的世界每天都在发生着深刻的变化.新操作系统的发布.CPU性能的提升.智能手机和平板电脑的流行.存储介质的变化.云的普及--这样的变化数不胜数. 在这样日新月异的时代中,"算法" ...

  7. 程序员的算法趣题Q50: 完美洗牌

    目录 1. 问题描述 2. 解题分析 2.1 思路1 2.2 思路2 3. 代码及测试 4. 后记 1. 问题描述 问题:对2n张牌洗牌,并求当1<=n<=100时,一共有多少个n可以使得 ...

  8. 程序员的算法趣题Q09: 落单的男女

    目录 1. 问题描述 2. 解题分析 3. 代码及测试 4. 思考 1. 问题描述 人们聚集在某个活动会场上,根据到场顺序排成一排等待入场,活动的主办人员,想把人们从队列的某个位置分成两组,想要让分开 ...

  9. 程序员的算法趣题Q22: 不缠绕的纸杯电话

    目录 1. 问题描述 2. 解题分析 3. 代码及测试 1. 问题描述 用绳子连接纸杯制作"纸杯电话"--这应该勾起了很多人对理科实验的回忆.如果把绳子拉直,对着一边的纸杯讲话,声 ...

最新文章

  1. 观点丨麦肯锡:怎么理解“人工智能最大挑战与机会”
  2. sql 将多个括号及内容删除_新浪微博将对逝者账号设置保护:不能登录、新发和删除内容...
  3. tf.where() 详解
  4. java函数求方程,Commons Math学习笔记——函数方程求解
  5. 静止的单摄像机无法得到像点的三维坐标详解
  6. 玩转 Rockchip 的开发板,这些信息你要知道
  7. ORACLE SQL获取时间字段
  8. 将Java项目从maven迁移到gradle
  9. 浅谈细说 JS 函数(call,apply,重载)
  10. 春节期间新闻回顾:思科微软多事 熊猫烧香完事
  11. python求解偏微分方程_Python数值计算----------求解简单的偏微分方程
  12. unity 字体 素材_unity中文字体制作工具
  13. Phodal 的 2018 节点:Think Big Be Long
  14. 阿里云部署flask项目
  15. Android和OpenCV的学习绘制几何图形
  16. c#语言中文编程下载,C#编程自学软件
  17. 大学操作系统期末考试复习经典计算题快速回顾
  18. LeetCode——字节跳动系列题目
  19. flyingsaucer转换多个html,Flying Saucer实现html转pdf(一些有关问题,持续更新)
  20. python好用的软件_比python好用的分析软件_财务分析报表怎么做_企业财务报表分析软件-帆软...

热门文章

  1. 用python一键保存几千张表情包斗图,分分钟征服朋友圈所有好友
  2. .NET使用MailKit进行邮件处理
  3. SpringBoot学习+秒杀项目
  4. [转]计算机编程语言的入门学习
  5. 信息的安全传输(JAVA实现信息的安全传输工具)
  6. 2021年激光雷达行业研究报告
  7. 阿里2015校园招聘三面总结
  8. sql-labs闯关32~37
  9. 认识Axure RP
  10. Vielleicht schreien Oma dick Bild einfach fest.Schreien Musik es halbe Sohn dick.