今天在做牛客网的摸底考试题的时候,遇到一道可能很经典的字符串算法题。
如果一个字符串可以通过移动字符的位置获得另外一个字符串,则称这两个字符串属于同一类。现在输入N个字符串,请判断里面有多少类字符串。
输入格式:第一行输入N,后面紧接着N行分别是N个字符串。每条字符串不超过50个字符。
输入示例:
4
abcd
bcda
accb
abdd
输出示例:
3
移动一条字符串,并不会改变一条字符串字符的数量。于是,问题就很好解了,算出一个字符串,分别有什么字符,和字符的数量,一条字符串相当于有了自己的一个签名,或者说是散列值。有着相同散列值的字符串就是同一类的字符串了。
里面用了一个最笨的办法,居然又重新合成一条字符串。。。
我之前看到过一个方法,利用质数来算签名,也就是他这个类的标识符。
因为一个数可以分为多个质数的乘积,也就是质因数分解。质数本身无法被其他数整除。因为一个数的质因数分解是唯一的。因此,质数乘积能够保证唯一,只与这条字符串有关,只要改变一个字符串,乘积就会变。我们赋给一个字符唯一一个对应的质数,比如’a’是2,’b’是3,’c’是5。若有一个字符串是acbb,则对应的乘积2*5*3*3=90
,有一个字符串是bbca的话,乘积=3*3*5*2=90
。算出的乘积一样,就是同一类了。
那好,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。
说到质数,看到过一篇算N以内质数的算法,有空写。
最后一个居然是101了。恩恩,那么,问题来了。
如果用int型来储存的话,老大难的溢出问题!如果long呢?一样!因为字符串最长50,如果50个z,想想100的50次方是多少。
那么,我用double型?我把所有的质数除以10或者100再算,可能可行吧,还没试过。但是也要注意浮点数的精度问题。字符串长了,运算多了,可能就会失去精度。
因为我想到,对int的乘法,溢出是溢出,但是其实只是没有了高位而已,低位的32个bit仍然保留着原本结果低位的32个bit。int的范围有-2147483648~2147483647这么多的数,遇到散列冲突的问题可能有,但是几率不大。
担心的话,直接上long。甚至BigDecimal。
不过这个算法还是很简陋,不够MD5,SHA-1这种算法冲突少。
给出一个简单的实现。
类似问题还有:
- 给出两条字符串,求出其中一条是否包含另外一条的所有字符。
- 字符串旋转问题:给出若干条字符串,其中如果能够通过字符串旋转变成另外一条字符串,则两条字符串为一类,求有多少类字符串
(文中的推理都是基于我的认识而来,并没有做验证。如果有错,欢迎指出)