串
串(String)是由零个或者多个字符组成的有序序列,又名叫字符串
空格串 :是只包含空格的串,注意它和空串的区别,空格串是有长度的,而且可以不止一个空格.
子串与主串,串中任意个数的连续字符组成的子序列称为该串的子串,相应地,包含子串的串称为主串
子串在主串的位置就是子串的第一个字符在主串中的序号
存储
数组和链表都能存
不过实际来看链表弊端很明显,一个字符对应一个节点,就会存在很大的空间浪费,若是存放多个字符,又考虑多少个问题,这都会影响串处理的效率,但是在连接串与串操作方便
总的来说:顺序存储灵活,性能比链表存储结构好
匹配BF算法
含义
BF (Brute Force) 的缩写,就是暴力算法,拆开字符一个个比较,通俗易懂,符合常理和第一反应,当然效率肯定慢啊,都暴力算法了.
截图君上场,简单介绍一下暴力算法,知道的可以直接忽略
时间复杂度
这个时间复杂度算起来也挺有意思的,故单独敲敲文字过一遍,主要查找的时候有特殊情况,会影响到时间复杂度的计算
设:主串为m
,子串为n
,
- 最好的情况:当然是一下就找到啊,字符串主串m
abcdefg
和子串nabc
,查找的次数一轮就通过为3次,根据大O时间复杂度计算1(<—不知道点这个1脚注),即为O(n)
- 稍微差点的情况: 例如代码的例子,O(主串+子串),即
O(m+n)
,因为所有字符都要一个个匹配,在遍历m
的基础上,再加上n
- 最坏的情况: 例如
000000000001
和001
,这种情况,每个字符都要匹配3
次,001
三个长度,直到匹配第三个1
的时候,才发现不是,所以时间复杂度是前面的匹配长度乘以后面的长度,这样一来时间复杂度O(n*(m-n+1))
,一般实际使用时m >> n,所以可以认为趋近于O(m*n)
代码
package com.company;
/**
* @Author: comfort
* @Date: 2020/8/4
*/
public class StringTest {
public static void main(String[] args) {
String source = "abcdefghijklmn";
String look = "jkl";
System.out.println(BF(source, look));
}
/**
* BF暴力检索
* @param source 原字符串
* @param lookupStr 要查找的字符串
* @return 成功返回下标,否则-1
*/
public static int BF(String source,String lookupStr){
//初始化从0开始的下标
int souIndex=0;
int lookIndex=0;
//转化char根据字符一一查找
char[] sourceChar = source.toCharArray();
char[] lookChar = lookupStr.toCharArray();
//思路 :判断时,要已两个字符的下标长度为条件
while (souIndex<sourceChar.length&&lookIndex<lookChar.length ){
System.out.println(souIndex+" "+sourceChar[souIndex]+" "+lookIndex+" "+lookChar[lookIndex] );
//两个字母相等
if (sourceChar[souIndex] == lookChar[lookIndex]) {
souIndex++;
lookIndex++;
}else{
//找不到 回退
//如果当前字符匹配不成功,则souIndex回溯到此次匹配最开始的位置+1处,也就是
//souIndex = souIndex - lookIndex + 1
souIndex=souIndex-lookIndex+1;
lookIndex=0;
}
}
if (lookIndex == lookChar.length) {
return souIndex - lookIndex;
}
return -1;
}
}
输出
0 a 0 j
1 b 0 j
2 c 0 j
3 d 0 j
4 e 0 j
5 f 0 j
6 g 0 j
7 h 0 j
8 i 0 j
9 j 0 j
10 k 1 k
11 l 2 l
9
当然,如果字符串不存在,一直会检索所有(13次)
比如我输入lmc
时,主串一直从头运行到最后,最后返回-1
0 a 0 l
1 b 0 l
2 c 0 l
3 d 0 l
4 e 0 l
5 f 0 l
6 g 0 l
7 h 0 l
8 i 0 l
9 j 0 l
10 k 0 l
11 l 0 l
12 m 1 m
13 n 2 c
12 m 0 l
13 n 0 l
-1
收获
- 第一次敲看着简单,撇到一边对着屏幕自己敲真有点费劲,敲过一遍就好多了
while
循环条件和初始化index
比较重要,书中写的是从1
开始,故重置算法是i=i-j+2
,看的我云里雾里,自己翻了资料,发现初始化index
从0
开始比较符合思路.- 重置步骤
i=i-j+1
这行代码比较直接给出来了,其实自己找个纸画画,目的就是j=0
时,让i
变回上一次开始的位置,多举两三个例子,就发现了i
和j
之间的关系. - 最后的返回下标,两个数字相减是可以倒推出来,也是看资料太直接了.最好动动脑.
其他:这篇有点水文字了…本来想和KMP算法写在一起,发现kmp篇幅太长,在kmp算法的基础,还是分开写.酱紫吧.
参考资料:
推导大O阶:
1.
用常数1取代运算时间中的所有加法常数2.
在修改后的运行数次中,只保留最高阶.3.
如果最高阶项存在且不是1,则去除与这个项的常数 ↩︎