KMP 是寻找字符串的子串出现次数的一种非常巧妙的算法, 无需对字符串依次和子串从头开始比较。
先对子串进行处理,用一个next数组求得当长度为j 时,最大的相同前缀和后缀的长度。
然后再对源字符串遍历,当源字符串和子串在某个字符上不同时,无需用源字符串的下一位和子串从头比较,
而是找到此时子串的后缀和前缀相同的位数, 在子串的该位数上和源字符串的下一位进行比较。
有一篇大牛的博客介绍的非常棒,很容易理解,强烈推荐初接触KMP的童鞋看看。
https://blog.youkuaiyun.com/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;
}