字符串匹配算法

BF算法

  • 概念介绍
      暴力匹配(BF)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较,直到得出最后的匹配结果。

  • 实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/***字符串匹配算法***/
#include<cstring>
#include<iostream>
using namespace std;

#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
#define MAXSTRLEN 255   		//用户可在255以内定义最长串长
typedef char SString[MAXSTRLEN+1];		//0号单元存放串的长度

Status StrAssign(SString T, char *chars) { //生成一个其值等于chars的串T
	int i;
	if (strlen(chars) > MAXSTRLEN)
		return ERROR;
	else {
		T[0] = strlen(chars);
		for (i = 1; i <= T[0]; i++)
			T[i] = *(chars + i - 1);
		return OK;
	}
}

//算法4.1 BF算法
int Index(SString S, SString T, int pos)
{
	//返回模式T在主串S中第pos个字符之后第s一次出现的位置。若不存在,则返回值为0
	//其中,T非空,1≤pos≤StrLength(S)
	int i = pos;
	int j = 1;
	while(i <= S[0]&&j <= T[0])
	{
		if(S[i]==T[j])
		{
			++i;
			++j;
		} //继续比较后继字符
		else
		{
			i=i-j+2;  //这地方要让i指针回溯到i-j+2这个位置
			j=1;
		} //指针后退重新开始匹配
	}
	if (j > T[0])
		return i - T[0];
	else
		return 0;
	return 0;
}

int main()
{
	SString S;
	StrAssign(S,"bbaaabbaba") ;
	SString T;
	StrAssign(T,"abb") ;
	cout<<"主串和子串在第"<<Index(S,T,1)<<"个字符处首次匹配\n";
	return 0;
}

KMP算法

KMP算法求解什么类型问题

  字符串匹配。给你两个字符串,寻找其中一个字符串是否包含另一个字符串,如果包含,返回包含的起始位置。
如下面两个字符串:

1
2
3
char *str = "bacbababadababacambabacaddababacasdsd";
char *ptr = "ababaca";
str有两处包含ptr 分别在str的下标10,26处包含ptr。
算法说明
  • 概念介绍
      一般匹配字符串时,我们从目标字符串str(假设长度为n)的第一个下标选取和ptr长度(长度为m)一样的子字符串进行比较,如果一样,就返回开始处的下标值,不一样,选取str下一个下标,同样选取长度为n的字符串进行比较,直到str的末尾(实际比较时,下标移动到n-m)。这样的时间复杂度是O(n*m)。
      KMP算法:可以实现复杂度为O(m+n)
    为何简化了时间复杂度: 充分利用了目标字符串ptr的性质(比如里面部分字符串的重复性,即使不存在重复字段,在比较时,实现最大的移动量)。
      KMP算法中,每当一趟匹配过程中出现失配时,主串S中的i指针不需要回溯,而是利用已经得到的“部分匹配”结果,将模式串向右“滑动”尽可能远的一段距离后,继续进行比较,从而快速达到匹配结果。
  • 模式串中next值的计算思想
      next数组的含义就是一个固定字符串的最长前缀和最长后缀相同的长度的值(它加不加1都可以,加1的原因是:例如当模式串是abcaba的时候,在匹配主串的时候,当j=6这个位置时(即第三个a的时候),next[6]的值就是3,在这时出现失配,j指针变为next[6]的值,即从字母c处重新比较,;若不加1就会从那个相同的那个字母开始继续比较)
    注意最长前缀:是说以第一个字符开始,但是不包含最后一个字符。
      比如:aaaa相同的最长前缀和最长后缀是aaa。
       abcjkdabc,那么这个数组的最长前缀和最长后缀相同必然是abc。
       cbcbc,最长前缀和最长后缀相同是cbc。
       abcbc,最长前缀和最长后缀相同是不存在的。

    对于目标字符串ptr,ababaca,长度是7,所以next[0],next[1],next[2],next[3],next[4],next[5],next[6]分别计算的是
    a,ab,aba,abab,ababa,ababac,ababaca的相同的最长前缀和最长后缀的长度。由于a,ab,aba,abab,ababa,ababac,ababaca的相同的最长前缀和最长后缀是“”,“”,“a”,“ab”,“aba”,“”,“a”,所以next数组的值是[-1,-1,0,1,2,-1,0],这里-1表示不存在,0表示存在长度为1,2表示存在长度为3。这是为了和代码相对应。

upload successful
next值的计算实例:
upload successful
具体的实现过程:
upload successful

具体实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/***字符串匹配算法***/
#include<cstring>
#include<iostream>
using namespace std;

#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
#define MAXSTRLEN 255   		//用户可在255以内定义最长串长
typedef char SString[MAXSTRLEN+1];		//0号单元存放串的长度

Status StrAssign(SString T, char *chars) { //生成一个其值等于chars的串T
	int i;
	if (strlen(chars) > MAXSTRLEN)   //strlen()只能用于char *s求长度,不能用于求字符串的长度。
		return ERROR;
	else {
		T[0] = strlen(chars);
		for (i = 1; i <= T[0]; i++)
			T[i] = *(chars + i - 1);
		return OK;
	}
}
//算法4.3 计算next函数值
void get_next(SString T, int next[])
{ //求模式串T的next函数值并存入数组next,没有用到next[0]
	int i = 1, j = 0;
	next[1] = 0;
	while (i < T[0])
		if (j == 0 || T[i] == T[j])
		{//即让next[2]的值是1
			++i;
			++j;
			next[i] = j;
		}
		else
			j = next[j];
}

//算法4.2 KMP算法
int Index_KMP(SString S, SString T, int pos, int next[])
{ 	// 利用模式串T的next函数求T在主串S中第pos个字符之后的位置的KMP算法
	//其中,T非空,1≤pos≤StrLength(S)
	int i = pos, j = 1;
	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()
{
	SString S;  //主串 
	StrAssign(S,"aaabbaba") ;
	SString T;  //模式串 
	StrAssign(T,"abb") ;
	int *p = new int[T[0]+1]; // 生成T的next数组,因为next[0]没有用,所以要声明一个长度为模式串长度加1的数组。 
	get_next(T,p);
	cout<<"主串和子串在第"<<Index_KMP(S,T,1,p)<<"个字符处首次匹配\n";
	return 0;
}

其中拓展优化的get_next函数为:

void get_nextval(SString T ,int nextval[]){
	int i=1;nextval[1]=0;j=0;
    while(i<T.length){
    	if(j==0||T.ch[i]==T.ch[j] {
        ++i;++j;
        if(T.ch[i]!=T.ch[j]) nextval[i ]=j;
        else nextval[i]=nactval[j];
        }
	}
    else j=nextval[j];
    }
}
  • 解决另一个小疑问:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
char类型指针和string类型数据可以实现下标访问元素值吗?
#include<iostream>
using namespace std;
int main(){
	char *p="xiaozhi";
	cout<<p[1]<<endl<<p<<endl<<*p<<endl;
	string s="xiaozhi";
	cout<<s[2]<<endl<<s;
} 
运算结果:
i
xiaozhi
x
a
xiaozhi
--------------------------------
Process exited after 0.3133 seconds with return value 0
请按任意键继续. . .

char *s和char s[]的区别:
char *s1="hello";//声明一个指针指向常量"hello"
char s2[]="hello";//在栈中开辟一个数组字符"hello";
/** s2[]="hello" 相当于
* char s2[6];
* s2[0]='h';
* s2[1]='e';
* s2[2]='l';
* s2[3]='l';
* s2[4]='0';
* s2[5]='\0';
*

说明:

s1内容不能修改,因为他指向常量
s2内容可以修改,因为他指代内存中的变量区
s2其地址和容量在生命期里不能改变


作为形参完全相同
例:
void function(char *s1);
void function(char s1[]);
*/
  • 这其中还有一种算法的改变就是再求next数组的时候用到next[0]。

upload successful

upload successful
实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void GetNext(int *next,const char *sub)
{
    next[0] = -1;
    next[1] = 0;
    int lensub = strlen(sub);
    int i = 2;//当前的i
    int k = 0;//前一项的K值
    while(i < lensub)
    {
        if(k == -1 || sub[i-1] == sub[k])
        {
            next[i] = k+1;
            i++;
            k = k+1;
        }
        else
        {
            k  = next[k];
        }
    }
}
int Kmp(const char *str,const char *sub,int pos)
{
    int i = pos;
    int j = 0;  //此时j=0就是用next[0]这个值,上面那个是j=1就不用next[0]这个值啦
    int lens = strlen(str);
    int lensub = strlen(sub);
    int *next = (int *)malloc(sizeof(int) * lensub);
    assert(next != NULL);
    GetNext(next,sub);
    while(j < lensub && i < lens)
    {
        if(j == -1 || str[i] == sub[j])
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];
        }
    }
    if(j >= lensub)
    {
        return i-j;
    }
    else
    {
        return -1;
    }
}

https://blog.csdn.net/f_zyj/article/details/51694610

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值