如何改进XOR方法解决异位词检测中的异或值冲突问题?
问题分析
这个问题我之前也踩过类似的坑!单纯用异或判断异位词确实容易栽在碰撞问题上——你的原方法只依赖异或运算的特性,但异或只能追踪每个字符出现次数的奇偶性,完全没法保证字符的具体出现次数一致。像你提到的"123"和"303",不同的字符组合刚好得到了相同的异或结果,自然就误判了。
改进方案:多维度线性校验
我们可以通过结合多个独立的线性统计量,在保持O(n)时间复杂度、不使用Map的前提下,彻底避免碰撞问题。这里给你两种实用的改进思路:
思路1:异或 + 累加和 + 平方和
同时计算三个统计量,只有当三个值完全相等时,才判定为异位词。这三个统计量从不同维度刻画字符串特征,几乎不可能出现不同字符串同时满足三者相等的情况。
代码实现
int isAnagram(char* str1, char* str2) { int xor1 = 0, xor2 = 0; long long sum1 = 0, sum2 = 0; long long sqSum1 = 0, sqSum2 = 0; int len = strlen(str1); for (int i = 0; i < len; i++) { // 异或校验:追踪字符出现的奇偶性 xor1 ^= str1[i]; xor2 ^= str2[i]; // 累加和校验:追踪字符的总ASCII权重 sum1 += str1[i]; sum2 += str2[i]; // 平方和校验:进一步增加区分度,避免累加和碰撞 sqSum1 += (long long)str1[i] * str1[i]; sqSum2 += (long long)str2[i] * str2[i]; } // 三个校验全部通过才返回1 return (xor1 == xor2) && (sum1 == sum2) && (sqSum1 == sqSum2); }
针对你提到的测试用例"123"和"303":
xor1='1'^'2'^'3'= 495051 = 50xor2='3'^'0'^'3'= 514851 = 48
两者的异或值直接不相等,就能正确返回0,避免误判。
思路2:双异或(字符本身 + 对应质数)
给每个字符分配一个唯一的质数,同时计算字符本身的异或值和对应质数的异或值。由于质数的唯一性,不同字符组合的质数异或结果碰撞概率极低,再结合字符本身的异或,几乎可以100%避免误判。
代码实现
int isAnagram(char* str1, char* str2) { int xorChar = 0, xorPrime = 0; // 预定义字符对应的质数(数字0-9 + 小写字母a-z,共36个) int primes[256] = {0}; // 初始化质数数组(仅第一次调用时初始化) if (primes['0'] == 0) { int p = 2; // 数字0-9对应前10个质数 for (char c = '0'; c <= '9'; c++) primes[(unsigned char)c] = p++; // 小写字母a-z对应后续26个质数 for (char c = 'a'; c <= 'z'; c++) primes[(unsigned char)c] = p++; } int len = strlen(str1); for (int i = 0; i < len; i++) { xorChar ^= str1[i] ^ str2[i]; xorPrime ^= primes[(unsigned char)str1[i]] ^ primes[(unsigned char)str2[i]]; } // 两个异或结果都为0则返回1 return (xorChar == 0) && (xorPrime == 0); }
这里直接异或两个字符串对应位置的字符(以及质数),最后判断结果是否为0,比分别计算再比较更简洁。
为什么这些方法有效?
异位词的核心要求是每个字符的出现次数完全相同:
- 异或校验保证了每个字符出现的奇偶性一致
- 累加和/平方和/质数异或则从不同角度保证了字符的“总量”或“唯一性”一致
多个校验维度结合后,不同的字符串几乎不可能同时满足所有校验条件,彻底解决了单纯异或的碰撞问题。
内容的提问来源于stack exchange,提问作者Miket25
相关产品推荐
相关产品推荐

