kmp中next数组的应用

首先加深以下对next数组的理解(kmp算法见基础算法专栏),next它的核心是这个公式:

next[j] = k;

它的含义是在模式字符串p中,[ 0 , j )这一子串的最长匹配数(前缀与后缀相同的最长个数)为k,所以若在p[j] 处失配,会回溯到next[k]进行匹配,举个例子:

abcgabcx
01234567
kj

字符串p在p[7]处失配,而p[7] = 3,说明[0,7)的最长匹配数为3,且为p[7]之前的3位和p[k]之前的三位。进一步拓展,可以研究next数组的前缀周期性,仍举个例子:

abcabcabcx
0123456789
kj

根据之前的描述,不难分析出p[9] = 6,与之前不同的是这个例子中的前缀具有周期性,周期单位为abc,周期次数为3,遵循下列公式

                             判断是否为周期:j%(j-k)== 0 (成立则为周期)                                                     周期次数 =  j/(j-k)                            

可以这样理解:若前缀有周期性,那么j-k必然为所有周期中的一个周期。

下面看一道例题:

Period

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 2398    Accepted Submission(s): 1187

Problem Description

For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (2 <= i <= N) we want to know the largest K > 1 (if there is one) such that the prefix of S with length i can be written as AK , that is A concatenated K times, for some string A. Of course, we also want to know the period K.

Input

The input file consists of several test cases. Each test case consists of two lines. The first one contains N (2 <= N <= 1 000 000) – the size of the string S. The second line contains the string S. The input file ends with a line, having the number zero on it.

Output

For each test case, output “Test case #” and the consecutive test case number on a single line; then, for each prefix with length i that has a period K > 1, output the prefix size i and the period K separated by a single space; the prefix sizes must be in increasing order. Print a blank line after each test case.

Sample Input

3

aaa

12

aabaabaabaab

0

Sample Output

Test case #1

2 2

3 3

Test case #2

2 2

6 2

9 3

12 4

思路:

构建next数组结合周期性判断即可,但要注意与kmp算法略微不同的是,构造的next的规模比字符串p的规模大1([ 0 , n]),因为最后一个字符也算入周期性判断,记录在next[n]处,而kmp算法不把字符串p整串当做其中一个子串,代码如下:

#include <bits/stdc++.h>
using namespace std;
char p[1000010];
int next[1000010];
int n;
void ccnext(int p_len);
void print(int p_len);
int main() {
	for(int i=1;;i++) {
		scanf("%d",&n);
		if(n==0) break;
		getchar();
		gets(p);
		printf("Tedt case #%d\n",i);
		ccnext(n);
		print(n); 
		memset(next,0,sizeof(next));
	}
	return 0;
}

void ccnext(int n) {//构建next数组 
	next[0] = -1;
	next[1] = 0;
	int i = 1;
	int k = 0;
	while(i<n) {//注意此处的边界 
		if(k<0 || p[k]==p[i]) {
			next[++i] = ++k;
		} else {
			k = next[k];
		}
	}
}

void print(int n) {
	for(int i=0;i<=n;i++) {
		if(next[i]==0 || next[i]==-1) 
			continue;
		else {
			int t = i-next[i];
			if(i%t==0) //整除即存在周期性 
				printf("%d %d\n",i,i/t);
		}
	}
}

(例题出自HUDOJ 1358)

以上~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值