在庞果网上的一个挑战题目:http://hero.pongo.cn/
回文字符串是指从左到右和从右到左相同的字符串,现给定一个仅由小写字母组成的字符串,你可以把它的字母重新排列,以形成不同的回文字符串。 输入:非空仅由小写字母组成的字符串,长度不超过100; 输出:能组成的所有回文串的个数(因为结果可能非常大,输出对1000000007取余数的结果)。 例如:输入"aabb" 输出为2(因为“aabb”对应的所有回文字符串有2个:abba和baab)
简单的分析之后: 长度为n的回文字符S串必须满足,S[i] = S[n-1-i], i>=0 因此:
所以很容易就可以字符计数判断输入字符串是否可以通过重新组合成回文字符串:
满足组合成回文字符串的条件后,我们再来分析这些字符可以有多少种不同的组合:
现在问题很清楚了,在n/2个元素进行组合排列:
这个题目是一个难度级别简单的题目,但如果自己没有准备充分的测试用例,很容易就会出错。
题目给出的用例“aabb”, 过于简单,当然我们可以自己补充一些:
下面这个用例长度是100 (<=100), 但是我们不可能自己计算出它的结果,无法直接用来当做测试用例。这也是我做错的原因,不知道怎么测试这个边界!
但实际上这个测试是非常关键,这个用例的需要计算(100/2)!这个值我们需要考虑用合适的类型来表示,而不是普通的integer, 如果是integer 将会溢出,结果肯定不对,但由于缺少测试用例,一般不好发现问题。
总结一下,测试是个硬伤!如果题目中提供这个边界测试用例,难度就大大降低了!
public static int palindrome(String s) { if (s == null || s.length() > 100 || s.length() < 1) { return 0; } int[] counts = new int[26]; for (int i = 0; i < s.length(); i++) { counts[s.charAt(i) - 'a']++; } if (!can(counts)) { return 0; } BigInteger divisor = BigInteger.ONE; for (int count : counts) { divisor = divisor.multiply(permutation(count / 2)); } BigInteger result = permutation(s.length() / 2).divide(divisor); return result.mod(BigInteger.valueOf(1000000007)).intValue(); } private static BigInteger permutation(int n) { if (n < 0) return BigInteger.ZERO; if (n == 0) return BigInteger.ONE; BigInteger result = BigInteger.ONE; while (n > 0) { result = result.multiply(BigInteger.valueOf(n)); n--; } return result; } private static boolean can(int[] counts) { int oddCount = 0; for (int count : counts) { if (count % 2 != 0) { oddCount++; } } return oddCount <= 1; }