字符串总结篇
Java字符串基础
Java对字符串的操作还是比较容易的
-
值得注意的是String对象的实现原理是
final char[]
这意味着String是不能改变的,只能重新赋值 -
String a = "a" 这个对象是储存在字符串常量池中
-
String a = new String("a") 这个对象储存在堆中
-
一般可以用StringBuilder来构造新字符串,注意StringBuilder线程不安全(因为StringBuilder有count成员变量,而在append函数对char[]数组操作时,会让count+=str.length(),这个加法操作不是原子操作,固线程不安全)
翻转字符串系列
在这道题中,541. 反转字符串 II每隔2k个字符的前k的字符,可能会把问题复杂化。其实当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。
只要让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间。
因为要找的也就是每2 * k 区间的起点,这样写程序会高效很多。
在151. 反转字符串中的单词中要求翻转字符串里的单词,这道题目可以说是综合考察了字符串的多种操作。是考察字符串的好题。
这道题目通过 先整体反转再局部反转,实现了反转字符串里的单词。
KMP系列
KMP算法因为是由这三位学者发明的:Knuth,Morris和Pratt,取了三位学者名字的首字母,所以叫做KMP。
KMP的核心就是通过前缀表跳过一部分之前已经匹配的文本内容,避免从头再去做匹配了。
什么是前缀表?
前缀表中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。
例如,对于”aba”,它的前缀集合为{”a”, ”ab”},后缀 集合为{”ba”, ”a”}。两个集合的交集为{”a”},那么长度最长的元素就是字符串”a”了,长 度为1,所以对于”aba”而言,它在前缀表中对应的值就是1。再比如,对于字符串”ababa”,它的前缀集合为{”a”, ”ab”, ”aba”, ”abab”},它的后缀集合为{”baba”, ”aba”, ”ba”, ”a”}, 两个集合的交集为{”a”, ”aba”},其中最长的元素为”aba”,长度为3。这个"aba"就是
最长相同前后缀。
如何计算前缀表?
前缀表的计算原理是pattern字符串自己和自己匹配,next[0] = -1是因为将next数组整体右移一位方便编程,有i,j两个指针,i负责遍历字符串,j负责遍历模式串,匹配成功则在next[]中写入当前的j值,并且i++,j++;匹配失败则将j的值设为next[j],意思是让j回退到最长相同前缀后缀的下一位。
public void getNext(String p, int[] next){
// 因为next表整体右移一位,所以next[0]取-1,方便编程
next[0] = -1;
// i指针为字符串指针,从1开始,因为next[0]已经设为-1
int i = 0;
// j指针为pattern串指针,从0开始
int j = -1;
while(i < p.length() - 1){
// j = -1 意味着上次匹配不成功,i指针需要移动,此时j指针同时移动,保证下次j==0
// 匹配成功则同时移动i,j指针 同时将j的值储存到next表中
if( j == -1|| p.charAt(i) == p.charAt(j)){
i++;
j++;
next[i] = j;
}else{
// 匹配不成功则将j设置为共同前缀的后面
j = next[j];
}
}
}
时间复杂度分析
其中n为文本串长度,m为模式串长度,因为在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是O(n),之前还要单独生成next数组,时间复杂度是O(m)。所以整个KMP算法的时间复杂度是O(n+m)的。
暴力的解法显而易见是O(n × m),所以KMP在字符串匹配中极大地提高了搜索的效率。