剑指 Offer II 032. 有效的变位词

剑指 Offer II 032. 有效的变位词

原题

给定两个字符串 st ,编写一个函数来判断它们是不是一组变位词(字母异位词)。

注意:*s**t* 中每个字符出现的次数都相同且字符顺序不完全相同,则称 *s**t* 互为变位词(字母异位词)。

  • 1 <= s.length, t.length <= 5 * 104
  • s and t 仅包含小写字母

解题思路

这道题和之前的 剑指 Offer II 014. 字符串中的变位词 / 剑指 Offer II 015. 字符串中的所有变位词 很类似。

多出的条件是判断 s 和 t 两个原字符串的字母顺序不完全相同, 而不是子串问题。

这道题指明了 s and t 仅包含小写字母, 所以我们还是可以用哈希表统计。

  1. 判断两字符串长度是否相同。
  2. 判断两个字符串是否 equal。
  3. 使用哈希表统计所有字母的出现次数;判断各个字母出现次数是否相同。

完整代码

	class Solution {
		public boolean isAnagram(String s, String t) {
			int len = s.length();
			if (len != t.length()) {
				return false;
			}
			char[] chars1 = s.toCharArray();
			char[] chars2 = t.toCharArray();
			
			// 以下三个循环可以使用并行优化性能
			boolean isEqual = true;
			for (int i = 0; i < len; i++) {
				if (chars1[i] != chars2[i]) {
					isEqual = false;
					break;
				}
			}
			if (isEqual) {
				return false;
			}
			int[] charsMap1 = new int[26];
			int[] charsMap2 = new int[26];
			
			for (char c : chars1) {
				++charsMap1[c - 'a'];
			}
			for (char c : chars2) {
				++charsMap2[c - 'a'];
			}
            // 当前循环与前面的循环有依赖关系
			for (int i = 0; i < 26; i++) {
				if (charsMap1[i] != charsMap2[i]) {
					return false;
				}
			}
			return true;

		}
	}

其他想说的

代码很简单, 但是代码确可以牵涉到很多性能优化点。

下面是一个更美观的代码。

class Solution {
		public boolean isAnagram(String s, String t) {
			int len = s.length();
			if (len != t.length()) {
				return false;
			}
			char[] chars1 = s.toCharArray();
			char[] chars2 = t.toCharArray();

			// 以下的循环可以利用多核并行计算结果
			// GCC 环境下优化更好
			// 使用自己编写的 equal 方法优化 String 自带的方法 
			if (stringEqual(chars1, chars2)) {
				return false;
			}
			int[] charsMap1 = new int[26];
			int[] charsMap2 = new int[26];

			for (char c : chars1) {
				++charsMap1[c - 'a'];
			}
			for (char c : chars2) {
				++charsMap2[c - 'a'];
			}
			for (int i = 0; i < 26; i++) {
				if (charsMap1[i] != charsMap2[i]) {
					return false;
				}
			}
			return true;

		}
		
		boolean stringEqual(char[] chars1, char[] chars2) {
			int len = chars1.length;
			for (int i = 0; i < len; i++) {
				if (chars1[i] != chars2[i]) {
					return false;
				}
			}
			return true;
		}
	}

CSAPP(深入理解计算机系统)(第三版) 从数的编码开始, 再到汇编指令, 再到机器指令, 再到 CPU 逻辑、流水线原理…第五章开始讲到了优化程序性能

有这么几条知识可以用到:

  • 利用当前计算机的多核, 最大化程序的并行执行能力。这一点, 可以通过对上述的三个循环及进行并行处理,因为它们的计算之间不会有依赖关系
  • 减少转移指令对编译器的影响, 同时提高流水线执行效率。这一点, 可以通过对比第一份和第二份代码,两个代码做了同一件事情;一个有方法调用, 一个顺序执行到底(当然, 这个点可以忽略, 因为这个不是影响程序性能的关键),涉及到了流水线的指令损失和编译器的优化。
  • 减少内存引用。这一点是关键, 虽然不知道 Java 会怎样优化以下的循环。
			// 编译器会优化吗?
			for (int i = 0; i < charsMap1.length; i++) {
				if (charsMap1[i] != charsMap2[i]) {
					return false;
				}
			}
			...
            // len 应当是保存在一个寄存器中的
            // C/C++ 的汇编也是这么做的
            int len = charsMap1.length;
            for (int i = 0; i < len; i++) {
				if (charsMap1[i] != charsMap2[i]) {
					return false;
				}
			}    

其他

本题解同步至剑指offer专项突击版仓库内, 欢迎查看。

我的力扣主页

Github仓库

Gitee仓库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值