KMP 基础(POJ3461)

KMP 是寻找字符串的子串出现次数的一种非常巧妙的算法, 无需对字符串依次和子串从头开始比较。

先对子串进行处理,用一个next数组求得当长度为j 时,最大的相同前缀和后缀的长度。

然后再对源字符串遍历,当源字符串和子串在某个字符上不同时,无需用源字符串的下一位和子串从头比较,

而是找到此时子串的后缀和前缀相同的位数, 在子串的该位数上和源字符串的下一位进行比较。


有一篇大牛的博客介绍的非常棒,很容易理解,强烈推荐初接触KMP的童鞋看看。

https://blog.csdn.net/starstar1992/article/details/54913261


看完后,最好做了下POJ的KMP题目,加深理解。

这里推荐POJ3461, 题目很长,其实只要看input 和out 介绍那里就知道是从一个源字符串里,求子串出现的次数。

直接上代码了,效率不高,刚接触不知怎么优化。


//2018 0421 pass 110ms
#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>

#define MAX_TEXT_SIZE  1000000
#define MAX_WORD_SIZE 10000 

char text[MAX_TEXT_SIZE + 3];
char word[MAX_WORD_SIZE + 3];
int next[MAX_WORD_SIZE + 3];

int textSize, wordSize;
int count;

void init() {
	int i;
	count = 0;

	for (i = 0; i < MAX_WORD_SIZE; i++) {
		next[i] = 0;
	}
}

int myStrLen(char *src) {
	int strLen = 0;
	while (*src != '\0') {
		strLen++;
		src++;
	}
	return strLen;
}

void calcNextForWord() {
	int i, j;

	for (i = 1; i < wordSize; i++) {
		j = i;
		while (j > 0) {
			j = next[j]; //往前回溯,j为前缀和后缀相同的个数
			if (word[j] == word[i]) { // 0 ~j 共有 j+1个
				next[i + 1] = j + 1; //到word[i]为止,前缀和后缀都相同的个数为j+1 
				break;
			}
		}
	}
}

void findSubStringCnt() {
	int i, j; // i:textIdx, j : wordIdx
	calcNextForWord();

	for (i = 0, j = 0; i < textSize; i++) {
		if (j < wordSize && word[j] == text[i]) {
			j++;
		}
		else {
			while (j > 0) {
				j = next[j];
				if (word[j] == text[i]) {
					j++;
					break;
				}
			}
		}
		if (j == wordSize) {
			count++;
		}
	}
}

int main() {
	int T;
	int testCase;

	//freopen("input.txt", "r", stdin);

	scanf("%d", &T);
	for (testCase = 0; testCase < T; testCase++) {
		init();
		scanf("%s", word);
		scanf("%s", text);

		wordSize = myStrLen(word);
		textSize = myStrLen(text);		

		findSubStringCnt();
		printf("%d\n", count);
	}

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值