刷题笔记之字符串

首先来说一下字符串

字符串不是一个基本数据类型(和int等不同)。他是java自己定义的一个类。

有以下几个常用方法:

1  .length()获取长度         (把这个和数组的.length区分开,数组获取长度是调用了属性,字符串是调用方法)

2   .charAt() 获取某个位置上的字符

3 .split()   将字符串以某个字符为依据切分为字符串数组

4 .subString()  输入两个参数,得到以第一个参数为头,第二个参数为尾的子字符串(字符串坐标从0开始)

5   构造方法                      在创建字符串时,可以有输入参数。若参数为字符数组,则可以得到这个数组构成的字符串

6    对于数组等结构,慎用toString方法。。一切类都会继承Object类,Object类自带方法toString。但这个toString方法得到的是 对象类名@16进制哈希值。

7    对于一个数组   如果想把它转化为字符串,可以调用方法   Arrays.toString。但是注意,这样的到的字符串是包含[]和,的

8   .toCharArray()  将字符串拆分为一个个字符组成的数组

9   删除字符串两端的空格可以用strip()   (java11版本及以后,老版本用trim)

其他

由于这次写题是以学习为目的的,所以补充一些写题时遇到的问题和思考

1   和String类似的,我们还有类StringBuilder和StringBuffer。注意,StringBuilder和StringBuffer并没有继承String类。String的长度是固定的,一经创建就无法改变,但StringBuilder类是长度不固定的,可以更快更简单地进行元素添加。并且StringBuilder类可以用toString变为String。

2   java支持多态,解释一下现阶段我的理解。java在定义一个对象时,一般是先声明引用变量,然后用new创建一个对象,然后赋值给引用变量。在这里,我们并不要求声明的类型和赋值的类型是一致的。(有相互继承的关系就行)

java中允许声明父类,赋值子类;也允许声明子类,赋值父类
当声明父类赋值子类时:这个变量不能访问子类新增的属性或方法(这些属性方法没有被声明)。只能调用父类的属性方法(即使父类是个接口,此时在实现方法时,用的是子类重写的方法,只是声明一样)
当声明子类赋值父类时:子类新增的属性和方法也不能使用。(这些属性方法没有被赋值)
也就是说,在引用属性或方法时,始终取小的部分。

写题

1  反转字符串

这题的思路比较简单,先把字符串变为字符数组,然后依次交换前后的元素就行(为了方便判断,可以考虑双指针)

2  反转字符串二

这题的重点实际上就不完全在字符串的反转上了。我们需要关注一下循环的条件。

3  替换空格

可以转化为字符数组依次比较替换,最终再将字符数组转为字符串输出。或者也可以将字符串转化为StringBuilder,然后用方法.setCharAt进行替换。

4  反转字符串中的单词

这题需要解决两个问题,一个是字符串的反转,另外一个是去掉多余的空格。实际上更麻烦的部分应该是如何更快地去掉空格。可以考虑使用双指针法解决冗余空格问题。

5 动态口令

很简单的题目,先拆分,再拼接就可以了

6 找出字符串中第一个匹配项的下标

KMP算法来了。

本题的目标是进行字符串的匹配。我们当然可以用暴力算法,但是不好。字符串匹配问题我们有很好的方法:KMP算法。纯纯的好东西。

KMP算法的基本思想是保存已经匹配过的字符。这是一个比较通常的想法,但实现起来还是有一定的技巧性的。比如说对于我们有haystack aabaabaafa,needle aabaaf。当比较这两个字符串时,我们首先将两个字符串从头匹配。匹配到第六位,也就是b和f时发生不匹配。这时,由于needle字符串本身是有一定特性的(这个特性接下来会说),因此我们不必从hatstack的第二位开始和needle匹配,而是可以直接将hatstack的第六位b和needle的第三位b比较。

上文中提到的特性就是他的最长公共前后缀并不为0. 我们可以利用这个特性极大减少比较次数。(暴力比较时实际上是有很多的冗余比较的)

对于 aabaa来说,我们以第一个a为头,最后一个a为尾为例计算他的最长公共前后缀(实际上需要以a为头,a,b,a,a,f依次计算一次)

以a为头的前缀为 a aa aab aaba (不包含aabaa)

以b为尾的后缀为 a aa baa abaa   (不包含aabaa)

容易发现他们的最长公共前后缀为aa 长度为2

在实际的应用中,我们定义一个next数组来储存needle的每一位的最长公共前后缀。在比较时,我们定义两个指针,一个指向haystack的头,一个指向needle的头,如果比较结果相同,两个指针同时后移一位继续比较。如果比较结果不相同,指向needle的头移动向他前一位的next数组中储存的数对应的下标位置(比如说,上例中b和f不同,此时指向needle的指针移动到needle[2])直到needle的指针指向第一位或比较值相等。

现在,剩下的问题就是应该如何计算next数组了。首先,next[0]由于只有一个数,他的最长公共前后缀一定为0。我们用for循环定义next数组的每个值。我们用双指针,一个为i,一个为j。i从1开始,j从0开始。如果相同,则ij同时后移1位,把j的值赋给next[i]。如果不同,就把j前移到上一位next对应的下标位置,继续比较,直到相等或前移到0。注意,在每一次循环结束后,j指向的是当前相等前缀的下一位。所以说如果下一次循环开始时,如果ij指向元素相同,就说明最长公共前后缀又长了一位。如果元素不相同,那就要找上一次的最长公共前后缀,我把他称为次最长公共前后缀,他就储存在next[j-1]里面。next[j-1]储存的是红字提到的前缀的最长公共前后缀,为了方便表达,我们称它为小前缀。类似地,我们考虑红字提到的当前相等前缀对应的当前相等后缀。他和前缀相等,所以说他的小后缀等于前缀的小前缀。这是我们从next[j-1]开始判断而非从头重新匹配的底气。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值