Anagrams 变位词

本文要讨论的是变位词,也就是说通过交换一个单词的各个字母的顺序能变成另一个单词,那么这两个单词互为变位词。

问题一:判断给定的两个单词(标准ASCII)是否变位词

方法一:用两个数组分别统计两个字符串里每个字符出现的个数,如果完全一致,则是变位词,否则不是.

这个方法是大小写敏感的。这个方法能支持的输入字符串的最大长度 将受 用来统计字符个数的数组的类型 的限制。如果用unsigned char[128]来统计字符个数,unsigned char最大值是255(0xFF),那么能支持的输入字符串最大长度是255。对于长度大于255的字符串将有可能出现误判。比如分别由256个"a"和256个"b"组成的两个字符串,统计结果都是全0,程序将误判为是变位词。如果改为用unsigned int[128],则能支持的输入字符串的最大长度是0xFFFFFFFF。但占用的内存是前者的4倍。

另外,下面的实现中将统计结果数组定义为static。理由一,不在调用栈上,可以避免在占用有限的stack空间,理由二,也没有在堆上,避免该函数被频繁调用的时候会产生大量的空间申请和释放的操作。(这两个理由成立吗?充分吗?)

bool IsAnagram1(const char *pStr1, const char *pStr2)
{
	static int stat1[128] = {0};
	static int stat2[128] = {0};
	
	memset(stat1, 0, sizeof(stat1));
	memset(stat2, 0, sizeof(stat1));
	
	const char *p = pStr1;
	while (*p != '\0') {
		stat1[*p]++;
		p++;
	}
	p = pStr2;
	while (*p != '\0') {
		stat2[*p]++;
		p++;
	}
	return (0 == memcmp(stat1, stat2, sizeof(stat1)));
}

方法二:利用这个原理:两个素数集合所含的素数和每个素数的个数都相等 当且仅当 每个集合所有元素的乘积相等。所以,令每个字符对应一个素数,分别计算每个字符串中所有字符所对应的素数的乘积。如果两个乘积相等,则是变位词。这个方法会受到数据最大值的限制。假设输入字符仅包含a-z的26个字符(大小写不敏感),则为了区分这26个字符,需要用到26个素数( 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101)分别对应a到z。最大值是101。另外我们需要一个足够大的数据类型来正确保存素数乘积,double型最大值为1.79769e+308。如果输入字符全部为对应素数101的那个字符z,那么最多可以支持153个。101^153小于DBL_MAX,101^154大于DBL_MAX。也就是说如果某个字符串含有154个z,那么素数乘积将越界,结果无法判断。

bool IsAnagram2(const char *pStr1, const char *pStr2)
{
	static const int PrimeNumber[26] = {2, 3, 5, 7, 11, 13, 17, 19, 23,
										29, 31, 37, 41, 43, 47, 53, 59,
										61, 67, 71, 73, 79, 83, 89, 97, 101};
	double f1 = 1.0;
	const char *pa = pStr1;
	while (*pa != '\0') {
		if (*pa >='A' && *pa <='Z')
			f1 *= PrimeNumber[*pa + ('a'-'A') - 'a'];
		else
			f1 *= PrimeNumber[*pa - 'a'];
		pa++;
	}
	double f2 = 1.0;
	const char *pb = pStr2;
	while (*pb != '\0') {
		f2 *= PrimeNumber[*pb - 'a'];
		pb++;
	}
	return f1 == f2;
}

对方法二的一种可能的改进是借用其他能够支持大数计算的Lib来完成素数的乘积。本文不再给出代码。

另一种改进可以将字符分组,分别判断两个字符串所含每一组字符是否一致。两个字符串是变位词 当且仅当 字符串所含每一组字符所对应的素数的乘积都分别相等。这样,可以增加能够判断的字符串的长度和字符范围。比如按大小写分组,即可实现26*2种不同字符组成的字符串的变位词判断,支持最大字符长度153(下面的代码给出了这个实现)。或者仍然不区分大小写,将26个字母分为a-m和n-z两组,每组13个字符,用到前13个素数,第13个素数是41,double最大值可以表示41的最高次方是191次方。即可实现26种不同字符组成的字符串的变位词判断,支持最大字符长度191。

bool IsAnagram3(const char *pStr1, const char *pStr2)
{
	static const int PrimeNumber[26] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
										31, 37, 41, 43, 47, 53, 59, 61, 67,
										71, 73, 79, 83, 89, 97, 101};
	double fUpcase1 = 1.0;
	double fLowCase1 = 1.0;
	const char *p = pStr1;
	while (*p != '\0') {
		if (*p >='A' && *p <='Z')
			fUpcase1 *= PrimeNumber[*p -'A'];
		else
			fLowCase1 *= PrimeNumber[*p - 'a'];
		p++;
	}
	
	double fUpcase2 = 1.0;
	double fLowCase2 = 1.0;
	p = pStr2;
	while (*p != '\0') {
		if (*p >='A' && *p <='Z')
			fUpcase2 *= PrimeNumber[*p -'A'];
		else
			fLowCase2 *= PrimeNumber[*p - 'a'];
		p++;
	}
	
	return (fLowCase1 == fLowCase2) && (fUpcase1 == fUpcase2);
}

问题二:输出给定单词的所有变位词

待续

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值