KMP

KMP算法

特别感谢 orz sofu6 让我悟了

(KMP) 算法指的是字符串模式匹配算法,要解决的问题就是在字符串(也叫主串)中的模式(pattern)定位问题

说简单点就是我们平时常说的关键字搜索。模式串就是关键字(接下来称它为P),如果它在一个主串(接下来称为T)中出现,就返回它的具体位置,否则返回-1(常用手段)。

暴力算法

从左到右一个个匹配,如果这个过程中有某个字符不匹配,就跳回去,将模式串向右移动一位

我们可以这样初始化:

之后我们只需要比较 (i) 指针指向的字符和 (j) 指针指向的字符是否一致。如果一致就都向后移动,如果不一致

(A)(E)不相等,那就把 (i) 指针移回第 (1) 位(假设下标从 (0) 开始),j移动到模式串的第(0)位,然后又重新开始这个步骤:

显然肯定会T掉,怎么优化??

KMP算法

利用已经部分匹配这个有效信息,保持 (i) 指针不回溯,通过修改 (j) 指针,让模式串尽量地移动到有效的位置

那么当发现一个字符与主串不匹配时,(j) 指针应该移向哪儿??

探究 (j) 移动的规律

C 和 D 不匹配了把 (j) 移动到哪儿??显然是第一位

下面情况相同

可以把 (j) 移到第2位,因为前面有两个字母已经匹配

得出KMP精髓

(j) 要移动的下一个位置 (k) 要保证最前面 (k) 个字符 (j) 之前的最后 (k) 个字符是一样的

(P[0 => k-1] == P[j-k => j-1])

另一种理解

若子串的前缀集和后缀集中,重复的最长子串的长度为(k),则下次匹配子串的j可以移动到第 (k) 位(下标为0为第0位)

在“aba”中,前缀集就是除掉最后一个字符'a'后的子串集合{a,ab},同理后缀集为除掉最前一个字符a后的子串集合{a,ba},那么两者最长的重复子串就是a,k=1;

在“ababa”中,前缀集是{a,ab,aba,abab},后缀集是{a,ba,aba,baba},二者最长重复子串是aba,k=3;

图解

发现 (C)(D) 不匹配 (j) 位前面的子串是 (ABA) ,该子串的前缀集是({A,AB}),后缀集是({A,BA}),最大的重复子串是(A),只有(1)个字符,所以j移到 (k) 即第1位

同理

(j) 位的时候,(j)前面的子串是(ABCAB),前缀集是({A,AB,ABC,ABCA},)后缀集是({B,AB,CAB,BCAB}),最大重复子串是 (AB),个数是(2)个字符,因此(j) 移到 (k) 即第2位。

匹配时的代码

void pre(){
	for(int i = 2,j = 0;i <= m; i++){
		while(j > 0 && b[i] != b[j + 1])j = P[j];//匹配失败,退步
		if(b[i] == b[j + 1]) j++;//匹配成功,继续匹配
		P[i] = j;
	}
} 

important

怎么求这些 (k) 呢??

因为在 (P) 的每一个位置都可能发生不匹配,所以我们要计算每一个位置 (j) 对应的 (k) ,用一个(P)数组保存(B[j] = k)当 A(主串)[i] != B[ j ]时,(j) 指针的下一个位置,因为下标从0开始的,(k) 值实际是 (j) 位前的子串的最大重复子串的长度

(P[j]) 的值(也就是(k))表示,当(B[j] != A[i])时,(j) 指针的下一步移动位置

我们用递推的思想

(B = “ababacb”)预处理,我们假设已经求出了(P[1],P[2],P[3],P[4],)(P[5],P[6])

    1 2 3 4 5 6 7
B = a b a b a c b
P = 0 0 1 2 ? ? ?  

很显然$ P[5] = P[4] + 1(,因为)P[4](可以知道)B[1……2](已经和)B[3……4]$相等了,现在又有 (B[3] = B[5]),所以(P[5])可以用(P[4])加一个字符得到所以如果在匹配过程中在(P[5])位置不同的时候,直接把(j)移到4(P[5] + 1)的位置进行比较

而接着看(P[6]) ,它显然不是(P[5] + 1),因为(P[P[5] + 1] != B[6])那么就要考虑“退一步”,将(j)退到(P[P[3] + 1])与A[i]比较,还不匹配,再退发现为(P[1] = 0),stop

注意

看清查找子串时,在主串中能不能交叉,举个例子:A = 'aaaaaa',B = 'aa',如果不能交叉,匹配完成就直接返回 (j = 0) ,如果允许在主串中重复时,如果返回 (j = 0) ,就会漏掉连续的(2,3|4,5)位置的子串aa;所以应该返回 (j = P[j]) ;

int ans = 0,j = 0;
for(int i = 1;i <= n; i++){
     while(j > 0 && a[i] != b[j + 1])j = P[j];
	if(a[i] == b[j + 1])j++;
	     if(j == m){
		ans++,j = 0;
	  }
    }
return ans;

模板体

/*
  work by:Ariel
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int A = 1e3 + 2;
int P[A], n, m;
char a[A], b[A];
void pre(){
	P[1] = 0;
	int j = 0;
	for(int i = 1;i < m; i++){
		while(j > 0 && b[i + 1] != b[j + 1])j = P[j];
		if(b[i + 1] == b[j + 1]) j++;
		P[i + 1] = j;
	}
} 
int kmp(){
	int ans = 0,j = 0;
	for(int i = 0;i < n; i++){
		while(j > 0 && a[i + 1] != b[j + 1])j = P[j];
		if(a[i + 1] == b[j + 1])j++;
		if(j == m){
			ans++; j = 0;
		}
	}
	return ans;
}
int main(){
  while(cin >> a + 1){
  	if (a[1] == '#')break;
  	scanf("%s",b + 1);
  	m = strlen(b + 1);
  	n = strlen(a + 1);
  	pre();
  	printf("%d
",kmp());
  }
  
}

Power Strings

hash可以水??

solution:

(KMP)一个简单的应用,要求主串中最多子串的个数,联想 (KMP) 自我匹配函数

不难发现如果(n % (n - p[n]) == 0)那就证明有子串,子串长度为(n - p[n]),所以最多子串个数为(n / (n - p[n]))

然后就套模板了

/*
  work by:Ariel
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int A = 1e6 + 5;
const int inf = 0x3f3f3f3f;
int p[A],n;
char a[A];
void pre(){
	p[1] = 0;
	for(int i = 1,j = 0;i < n; i++){
		while(j > 0 && a[i + 1] != a[j + 1])j = p[j];
		if(a[i + 1] == a[j + 1])j++;
		p[i + 1] = j;
	}
}
int main(){
   while(1){
   	   scanf("%s",a + 1);
   	   if(a[1] == '.')break;
   	   n = strlen(a + 1);
   	   pre();
   	   if(n % (n - p[n]) == 0) printf("%d
",n / (n - p[n]));
   	   else printf("1
");
  }
}

Radio Transmission

solution:

结论题
(ans = n - p[n])

/*
  work by:Ariel
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int D = 1e6 + 5;
char a[D];
int p[D], n;
void pre(){
	p[1] = 0;
	for(int i = 1,j = 0;i < n; i++){
	    while(j > 0 && a[i + 1] != a[j + 1])j = p[j];
	    if(a[i + 1] == a[j + 1]) j++;
	    p[i + 1] = j;
	}
}
int main(){
   scanf("%d%s", &n,a + 1);
   pre();
   printf("%d",n - p[n]); 
}

原文地址:https://www.cnblogs.com/Arielzz/p/14259629.html