字符串的存储结构
字符串的存储结构与线性表相同,也分顺序存储结构和链式存储结构。
字符串的顺序存储结构是用一组地址连续的存储单元来存储串中的字符序列的。
BF算法——Brute Force 属于朴素的模式匹配算法(效率低下):
–有两个字符串S和T,长度为N和M。首先S[1]和T[1]比较,若相等,则再比较S[2]和T[2],一直到T[M]为止;若S[1]和T[1]不等,则T向右移动一个字符的位置,再依次进行比较。
–该算法最坏情况下要进行M*(N-M+1)次比较,时间复杂度为O(M*N)。
int Index(String S,String T,int pos)
{
int i = pos; //i用于主串S中当前位置下标
int j = 1; //j用于子串中当前位置下标
//0号下标存放字符串的长度
while( i<= S[0] && j <= T[0])
{
if(S[i] == T[i])
{
i++;
j++;
}
else // 若失配则j回溯到第一个元素从新匹配
{
i = i-j+2; // i回溯到上次匹配首位的下一个元素
j = 1;
}
}
if(j >T[0])
{
return i-T[0];
}
else
{
return 0;
}
}
KMP算法
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。
在KMP算法中,对于每一个模式串事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。
给模式匹配串添加一个k数组(也就是KMP中非著名的next()函数,函数本身包含了模式串的局部匹配信息)。
重点在于next()函数的理解:NEXT数组——当模式匹配串T失配的时候,NEXT数组对应的元素指导应该用T串的哪个元素进行下一轮的匹配。
可以尝试给出一串模式串验证得到的next值
如下
T | 5 | a | b | a | b | a | a | a | b | a |
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
next |
| 0 | 1 | 1 | 2 | 3 | 4 | 2 | 2 | 3 |
第一位的next值为0,第二位的next值为1,后面求解每一位的next值时,根据前一位进行比较。
void next(String T,int *next)
{
//假设i是后缀下标
//j是前缀下标
int j = 0;
int i = 1;
next[1] = 0;
while(i < T[0])
{
if(j==0 || T[i] == T[j])
{
i++;
j++;
next[i] = j;
}
else
{
//回溯
j = next[j];
}
//前缀是固定的,后缀是相对的
}
}
利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置。
#include<stdio.h>
typedef char* String;
void get_next(String T,int *next)
{
//假设i是后缀下标
//j是前缀下标
int j = 0;
int i = 1;
next[1] = 0;
while(i < T[0])
{
if(j==0 || T[i] == T[j])
{
i++;
j++;
next[i] = j;
}
else
{
//回溯
j = next[j];
}
//前缀是固定的,后缀是相对的
}
}
//返回子串T在主串S第pos个字符之后的位置
//若不存在,则返回0
int Index_KMP(String S,String T,int pos)
{
int i = pos;
int j = 1;
int next[255];
get_next(T , next);
while(i<=S[0] && j<= T[0])
{
if( j==0 || S[i] == T[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if( j >T[0])//模式匹配最后一个元素之后
{
return i-T[0];
//匹配成功,返回位置
}
else
{
return 0;
}
}
int main()
{
return 0;
}