基本概念和储存结构
字符集V上字符组成任何有限序列
如C语言中的“abcd"
可以看作是一个特殊的线性表:(只含有字符型变量)一般用顺序储存结构来储存表
运算
#include<stdio.h>
#define MAXN 1000
char s[MAXN], s1[MAXN], s2[MAXN];
const int fail = 0, false = 0;
const int suc = 1, true = 1;
int strlen(char s[])
{
int i;
for(i = 0; s[i] != 0; i++){}
return i;
}
int strcat(char s1[], char s2[])//s1 += s2
{
int l1 = strlen(s1);
int l2 = strlen(s2);
if(l1 + l2 >= MAXN)
return fail;
for(int i = 0; i <= l2; i++)// '\0'要考虑进去
s1[l2 + i] = s2[i];
return suc;
}
int strsub(char s1[], int pos, int len, char s2[])
{
//s1的pos位置取len长度的字符变成s2
int l1 = strlen(s1);
int l2 = strlen(s2);
if(pos < 0 || pos > l1)
return fail;
if(len < 0 || pos + len > l2)
return fail;
for(int i = 0; i < len; i++)
s2[i] = s1[pos + i];
s2[len] = 0;
return suc;
}
int strins(char s1[], int pos, char s2[])
{
//s2插在s1的pos位置上
int l1 = strlen(s1);
int l2 = strlen(s2);
if(pos < 0 || pos > l1 || l1 + l2 > MAXN)
return fail;
for(int i = l1; i >= pos; i--)
s1[l2 + i] = s1[i];
for(int i = 0; i < l2; i++)
s1[pos + i] = s2[i];
return suc;
}
int strdel(char s1[], int pos, int len)
{//pos删去len个字符
int l1 = strlen(s1);
if(pos < 0 || pos > l1)
return fail;
if(pos + len >= l1)
s1[pos] = 0;
else
for(int i = 0; i <= l1 - pos - len; i++)
s1[pos + i] = s1[pos + i + len];
return suc;
}
匹配
BF算法
拿小的串和大的串对齐,每次遇见不匹配的情况向后移动一格,再一一匹配
算法复杂度为O(nm)可以看成O(n²)
KMP算法
这里不匹配的是a和f,那么我们应该找8字符串的f前面的c来判断要移动多少步,如果直接移动6步,就会缺失掉移动三步时候刚好匹配的情况
如何找短的字符串每个位置对应移动的步数?
这时候我们需要观察字符串的情况了
当f出现匹配失误的时候,我们就得看前面abcabc的部分来看“后缀”和“前缀”是否一样
不难发现当长度为3的时候,前缀和后缀是一样的,这个时候,我们要往后移动6-3=3步,保证你下一次匹配的时候,abc这部分仍然可以匹配上前面部分的abc,此时从第四个位置a开始一一比对
(长度为6的子集必定相等,这个时候是移动6-6=0步,没有意义)
所以我们很容易得出子串每个位置对应的next比对内容
(注意:若为i匹配不上,下一个匹配的是next[i-1],也就是i-1的数字对应的前后缀)
“” 当第一个字符不匹配,我们默认为-1(也可以是0,只是差一个数字罢了)
a,公共前后缀是他自身,往后移动 1 - 0 = 1步,光标移动到a上 为 0
ab, 公共前后缀只有2(本身),所以我们可以认为没有公共前后缀,移动的大小为 2 - 0 = 2步,在程序上就是把比对的光标移到a上 为 0
abc 同上,移动 3 - 0 = 3,把光标移动到a上 0
abca 前后缀最大为1(把等于本身的去掉),所以移动 4 - 1 = 3步,把光标移动到b 1
abcab 前后缀最大为2,所以移动 5 - 2 = 3步,把光标移动到c 2
abcabc 前后缀最大为 3,移动 6 - 3 = 3 步,光标移动到c
于是我们得到如下表格
、
得到next数组代码
KMP优化
(待补充
BM算法
检验26个字母(可以把标点符号和数字也加上)在短串的位置 (自右向左):
有如下情况
①字母不在短串中 || 字母是短串的最后一个位置 : 往右移动s.length()
②字母在短串中且不是最后一个位置 往右移动 s.length() - i - 1 (这的i是字母在子串中出现的最大位置)
比如return这个子串,r出现在0位和4位,取4
于是 f(n) = 6, f( r ) = 4, f(u) = 3 f(t) = 2 f(e) = 1,其他所有字母 = 6
当你不匹配的时候,匹配的子串每次移动 的是, 子串上一次最后一个字符 对应位置的 主串位置的alpha
举个例子
retuenereturn
return
这里n对应的上,但是r对应不上,这里往右移动的应该是f(n) = 6
reeurnnnreturn
return
这里e对不上,往右移动也是看最后一个字符n,f(n) = 6
BM和KMP算法都减少了许多不必要的匹配,可以将时间复杂度缩小到O(n)