字符串中的字串问题,两种算法了解。以及自己这两天看到的些东西的总结

  问题描述我就不说了,就说其实就是JAVA里面String.indexOf().

  第一种是关于KMP算法的,甘草上半年的时候让我去看的,好像现在想想也没有忘记

  其实啊,我觉得这个算法的主要思想很简单,也很好:对子字符串进行分析先,得到一个从子字符串生成的数组,作用呢?呵呵,我先卖个关子,看具体的字符串的时候就知道有什么用了。

  首先我来说说最简单的吧,比如hellhello,我要匹配的子字符串是hello.怎么匹配呢?

  h   e   l    l  h  e  l    l  o

  |     |    |    |   |

  h   e    l   l  o

 这里出现了不匹配,那我们就应该rollback对吧,那从哪里开始呢?从hellhello(第2位)开始?不是吧,这样的话,我们假设原 字符串是N,子串是M,算法复杂度就应该是O(M*N);但是明明不对啊,我们应该是从hellhello开始查啊,前面都不合适啊,明明都查过了啊。这样快多了嘛。。。。

   但是请考虑这样的情况:eeel------eel这样的情况,我们怎么在rollback以后重新开始新的查找呢?

   这个时候我们应该想起原来说的需要分析子串生成的那个数组了,也就是我需要去跳跃前面查找过的字符,但是到底跳跃多少呢?其实是由子串决定的,至于具体这个数组的生成规范呢,我就不说了,其实是留给自己的思考,让自己以后回来看的时候能够好好思考,而不是只看。

    顺便说下,KMP的算法复杂度是在O(N+M),即便是在最坏情况下

  

 

 

    第2种是我翻阅源代码的,JAVA,没技术含量多了。。。。

    for循环里找子串的第一个字符,找到以后再匹配剩下来的字段,不过原理和感情上都比较容易接受。但是感觉最坏情况下算法复杂度还是O(N*M),但是平均复杂度是一定优于第一直觉的。平均的情况我没有去算,因为平均的算起来真是麻烦。。哈哈,大家体谅啊。。。

 

 

   其实KMP算法的这样一种思想在算法中间还是很常见的,比如今天看到的一个问题的解答就是:给你一个dictionary(文件形式),给你一个单词,让你找出他们一系列的单词(形容词不会翻译,勉强理解意思吧),pots跟stop,tops是属于一个系列的(他们由完全一样的字母形成)。当单一的一个单词的时候,也许遍历是不错的选择,但是当面临一次性N多,或者说是网络情境下使用的时候,我们就会发现,效率在急剧下降。算法复杂度为O(N*M)(N为字典中总单词数,M为输入的单词数目);而资料给出的方案就是一次遍历dictionary,将所有的单词贴上一个标签。这个标签是将组成这个单词的字母按字典顺序排列起来,比如pots的标签是opst,同样stop的标签也是opst。全部打完标签以后,对标签排序,这样所有的组成字母相同的单词就排列在一起了。最后我们查找的时候就可以利用二分法查找了。 这样的算法复杂度呢不大会算了,哈哈,总时间是N+Nlog(N)+M*log2(N)。这样的时间随着M的增大,与每次都进行遍历的方法间的优势将越来越大。 

   这个时候我们将需要一个trade off。代价和收益问题。让我想起了最近总是想进百度的事情,所以总是在看百度以前的笔试题目,有一道题目是这样的:93=78+7+8;我们给98这样的数取名叫OOXX数,然后让你输入0-1000内一个数,编程判断这个数字是不是OOXX数。

   ^_^,我的第一想法是99乘法表。。怎么说呢,既然说清楚只在0-1000内,那我就应该利用这个限制条件,生成一个1000的数组,做一次遍历,从1开始,比如2=1+1;11=10+1;所有被得到的数字,直接用INDEX方法把他标记为1,不然就是0;然后你输入多少就可以用数组访问,直接知道咯。简单,问花费多少时间代价呢?2000(其中1000是用来初始化数组的)

  这样的做法的灵感来自于小时候乘法表总是背不出来,^_^,不要鄙视我哦~~~~。优点嘛,当你需要查询的数字很多的时候每个数字都只需要1就可以直接访问到,有没有点HASH的味道在里面呢?

    但是要是我只查一次呢,建这么大一个OOXX数字表是不是就太夸张了点,或者,当我需要1-1000000间的时候,是不是我也建这么大一张表,而其实我只要查询一次呢?

   感觉被耍了。。。再往小时候想想,这个时候怎么办?还能怎么办,老老实实算啊,但是怎么算啊?很显然是个特征数,那就吧特征写出来吧。。。我写写我看出来的特征吧,大家也可以写自己的特征:

                                                 一个OOXX数

                                                                            N=XYY+X+Y+Z=101X+11Y+2Z(如果他是一个3位数处理得到的话)

    有点意思了吧,为什么呢?11Y+2Z是不是有取值范围?多少?0-117,那就简单了,X=N/100,或者X=N/100-1,X=N/100-2,这样范围就减少多了,然后呢?2Z的取值范围是多少?0-18啊,那Y的取值呢?还是3种情况,Z是不是就呼之欲出了。

  好嘛,那我的这个算法的算法复杂度是多少呢?3^(lgN-1),根据输入数的位数决定,3位就是9次尝试,4位就是27次尝试。

   算法出来以后的第一感觉是,1000以内也没什么,但是随着数位的增加,我总觉得不是特别好,lgN是可以接受的,但是作为3的指数,就有些让人接受不了。那怎么办啊,^_^,不要着急,再想想办法呢。把式子改变下呢。

                                                                         N=XYY+X+Y+Z         -----》   N-XYZ=X+Y+Z;

   这意味着什么呢?我靠,那就是说N和XYZ的差值一定小于等于27呢?想想,对啊,而且,我们还可以把范围缩小,怎么缩小,想想看吧,X是不是一定小于等于N/100,对啊,那我们就可以把尝试的范围缩小到N-1到N-N/100-18,这几个数字之间了。

  现在的算法复杂度是多少呢?O(9*lgN)啊。

  这样的复杂度,对我而言,可以安心了。毕竟已经从一个指数型变成了底数型了。。。

  这就是我这两天看到的东西吧,还有些没有完全写出来,不过先就这样吧,回笼觉去了。。。哈哈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值