leetcode每日一题之找不同
题目链接:https://leetcode-cn.com/problems/find-the-difference/
题目描述:
给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。
示例 1:
输入:s = "abcd", t = "abcde"
输出:"e"
解释:'e' 是那个被添加的字母。
示例 2:
输入:s = "", t = "y"
输出:"y"
示例 3:
输入:s = "a", t = "aa"
输出:"a"
示例 4:
输入:s = "ae", t = "aea"
输出:"a"
题解1:
使用哈希表进行计数抵消
//输入:s = "abcd", t = "abcde"
//输出:"e"
//解释:'e' 是那个被添加的字母。
public static char findTheDifference(String s, String t) {
int sizeS = s.length();
int sizeT = t.length();
if (sizeS == 0) {//如果s的长度为0,直接取出t的第一个元素即可
return t.charAt(0);
}
int[] table = new int[26];
for (int i = 0; i < sizeT; i++) {
if (i < sizeS) {//对s进行处理
table[s.charAt(i) - 'a']++;//数组存放的是元素出现的次数
}
table[t.charAt(i) - 'a']--;//把s中的元素抵消
}
for (int i = 0; i < 26; i++) {
if (table[i] != 0) {
return (char) ('a' + i);
}
}
return 'a';
}
//输入:s = "abcd", t = "abcde"
//输出:"e"
//解释:'e' 是那个被添加的字母。
public static char findTheDifference2(String s, String t) {
char[] charArrays = s.toCharArray();
char[] charArrayt = t.toCharArray();
if (charArrays.length == 0) { //对应s = "",t = "a"这种情况
return charArrayt[0];
}
int[] table = new int[26];//定义一个数组存放字符的索引
for (int i = 0; i < charArrayt.length; i++) {
if (i < charArrays.length) {
table[charArrays[i] - 'a']++;
}
table[charArrayt[i] - 'a']--;
}
for (int i = 0; i < 26; i++) {//经过抵消后,数组中不存在重复的元素都为0,不为0的就是最后新加的那个数组所在的索引位置
if (table[i] != 0) {
return (char) ('a' + i);
}
}
return 'a';
}
解法2:位运算
class Solution {
public char findTheDifference(String s, String t) {
int ret = 0;
for (int i = 0; i < s.length(); ++i) {
ret ^= s.charAt(i);
}
for (int i = 0; i < t.length(); ++i) {
ret ^= t.charAt(i);
}
return (char) ret;
}
}
位运算
关于异或:https://blog.csdn.net/qq_19272431/article/details/78564391
在逻辑学中,逻辑算符异或(exclusive or
)是对两个运算元的一种逻辑析取类型,符号为 XOR 或 EOR 或 ⊕(编程语言中常用^
)。但与一般的逻辑或不同,异或算符的值为真仅当两个运算元中恰有一个的值为真,而另外一个的值为非真。转化为命题,就是:“两者的值不同。”或“有且仅有一个为真。”
1 ⊕ 1 = 0
0 ⊕ 0 = 0
1 ⊕ 0 = 1
0 ⊕ 1 = 1
根据定义我们很容易获得异或
两个特性:
恒等律:
X ⊕ 0 = X
归零律:X ⊕ X = 0
应用:
快速比较两个值是否相等:
先让我们来一个简单的问题;判断两个int数字a,b是否相等,你肯定会想到判断a - b == 0
,但是如果判断a ^ b == 0
效率将会更高
交换两个值:
a = a ^ b;
b = a ^ b; //a ^ b ^ b = a ^ 0 = a;
a = a ^ b; //a ^ a ^ b = 0 ^ b = b;
在评论区还发现了一种比较炫技的写法:
利用 Java 的 stream 操作,详解如下:
-
先字符串拼接起来
-
String 通过 chars 算子转为 IntStream
-
利用 reduce,异或所有的值。最后转为 char 类型。
//String s = "cedb";
//String t = "edbcb";
public static char findTheDifference3(String s, String t) {
//a ^ a = 0,0 ^ a = a
return (char) (s + t).chars().reduce(0, (a, b) -> a ^ b);
}
reduce
学习链接:https://www.liaoxuefeng.com/wiki/1252599548343744/1322402971648033
public static void main(String[] args) {
int sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(0, (acc, n) -> acc + n);
System.out.println(sum); // 45
}
可见,reduce()
操作首先初始化结果为指定值(这里是0),紧接着,reduce()
对每个元素依次调用(acc, n) -> acc + n
,其中,acc
是上次计算的结果:
// 计算过程:
acc = 0 // 初始化为指定值
acc = acc + n = 0 + 1 = 1 // n = 1
acc = acc + n = 1 + 2 = 3 // n = 2
acc = acc + n = 3 + 3 = 6 // n = 3
acc = acc + n = 6 + 4 = 10 // n = 4
acc = acc + n = 10 + 5 = 15 // n = 5
acc = acc + n = 15 + 6 = 21 // n = 6
acc = acc + n = 21 + 7 = 28 // n = 7
acc = acc + n = 28 + 8 = 36 // n = 8
acc = acc + n = 36 + 9 = 45 // n = 9
因此,实际上这个reduce()
操作是一个求和。
如果去掉初始值,我们会得到一个Optional<Integer>
:
Optional<Integer> opt = stream.reduce((acc, n) -> acc + n);
if (opt.isPresent()) {
System.out.println(opt.get());
}
这是因为Stream
的元素有可能是0个,这样就没法调用reduce()
的聚合函数了,因此返回Optional
对象,需要进一步判断结果是否存在。
Optional<Integer> sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce((acc, n) -> acc + n);
System.out.println(sum); // 45
if (sum.isPresent()) {
System.out.println(sum.get());
}
利用reduce(),我们可以把求和改成求积,代码也十分简单:
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
int s = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(1, (acc, n) -> acc * n);
System.out.println(s); // 362880
}
}
注意:计算求积时,初始值必须设置为1
。
String[] array = "Stream API supports functional-style operations".split("");
String result = Arrays.stream(array).map(str -> str.toLowerCase()).reduce((acc, str) -> acc + "~" + str).get();
System.out.println(result);
// 按行读取配置文件:
List<String> props = Arrays.asList("profile=native", "debug=true", "logging=warn", "interval=500");
Map<String, String> map = props.stream()
// 把k=v转换为Map[k]=v:
.map(kv -> {
String[] ss = kv.split("\\=", 2);//第二个参数表示分割的份数
return Collections.singletonMap(ss[0], ss[1]);
})
// 把所有Map聚合到一个Map:
.reduce(new HashMap<String, String>(), (m, kv) -> {
m.putAll(kv);
return m;
});
// 打印结果:
map.forEach((k, v) -> {
System.out.println(k + " = " + v);
});
// String sss = "The rain in Spain falls mainly in the plain.";
// // 在每个空格字符处进行分解。
// String[] ssss = sss.split(" ", -1);//第二个参数表示分割的次数,-1表示无限制
// for (String s : ssss) {
// System.out.println(s);
// }