萌新的看毛片(kmp)学习日记(下标从0开始)

bf匹配算法的思想是每次主串和模式串都从0开始匹配,当出现失匹的时候模式串将回退到起点,主串将回退到本次匹配起点的下一位置然后重新进行匹配,这样的匹配方式虽然好理解但是时间复杂度比较高,而kmp每次出现失匹的情况只有模式串回退,主串不回退,如果当前失匹点的位置为s[i],并且前k个元素和从头往后数k个元素匹配(即:"s[0]s[1]s[2]…s[k-1] = "s[i-k].....s[i-1]"),那么主串从上一位置一直往前k个元素和s[0]到s[k--1]肯定是匹配的,所以只需要比较当前位置和模式串的k位置是否匹配就可以了.

 

 

/*
	a为主串,b为模式串,n为主串长度,m为模式串长度,从主串的第pos位置开始往后比较
*/
int kmp(char a[], char b[], int n, int m, int pos)
{
	int i = pos, j = 0;//将主串的初始位置指向pos位置,模式串从头开始比较
	while(i < n)
	{
		if(j == -1 || a[i] == b[j])//如果当前位置匹配,那么继续匹配下一位置;
		{
			/*
			j == -1的情况是如果上次在某位置失匹并且模式串中没有一个k位置使得模式串中当前位置的前k个位置和从头开始k个位置相匹配
			那么就让j回退到-1直接从主串的下一个点和模式串的第一个点进行匹配
			*/
			++i;
			++j;
		}
		else
			j = next[j];//如果失匹模式串回退到k位置继续进行匹配(即:next[j]位置)
		if(j >= m)//如果j匹配到m,说明主串在模式串中完全匹配成功,模式串在主串中的起始下标就是i-m(第i-m+1个位置从零开始数就是i-m从1开始数就是i-m+1)
			return i - m;
	}
	return 0;
}

 

 

 

 

 

个人感觉如果理解了kmp主串和模式串匹配的原理,next数组的求解就比较好理解了,因为next数组的求解过程就是模式串自我匹配的过程

 

 

/*
	求next数组其实就是模式串自我匹配的过程,
*/
void get_next(char b[], int m)//b数组为模式串,模式串长度
{
	int i = 0, j = -1;
	next[0] = -1;//第一个位置前面不可能出现k个元素匹配所以直接回退到-1
	while(i < m)
	{
		if(j == -1 || b[i] == b[j])
		{
			++i;
			++j;
			next[i] = j;//从0到j-1位置肯定和i-k到i-j位置是匹配的所以如果模式串在i位置失匹只需要回退到j位置就可以了
			/*
				举个板栗,假设当前的j为jk,如果前jk不匹配,那么在某个位置j就回退了,j根本自加不到jk...
			*/
		}
		else//同主串和模式串匹配时的回退原理,回退到next【j】(k)位置继续进行匹配
			j = next[j];
	}
}

 

 

 

如果模式串为“aaaab”主串为“aaabaaaab”匹配时,当i = 3,j = 3时a【i】≠ b【j】,由前面计算的next还需要进行i = 3(j = 2,j = 1, j = 0)三次多余的比较,

 

next[j] = k; 而模式串中bj = bk,当主串中字符ai和pj比较不想登时,不需要再和bk进行比较,而直接和b【next【k】】进行比较,此时的next【j】和next【k】应该相同

所以next数组的求解可以改进成

 

 

void get_next(char b[], int m)`
{
	int i = 0, j = -1;
	next[0] = -1;
	while(i < m)
	{
		if(j == -1 || b[i] == b[j])
		{
			++i;
			++j;
			if(b[i] == b[j])
				next[i] = next[j];
			else
				next[i] = j;
		}
		else
			j = next[j];
	}
}

 

 

 

 

 

 

你可能感兴趣的:(萌新的看毛片(kmp)学习日记(下标从0开始))