POJ-3461 Oulipo
题目链接:POJ-3461
题目大意:寻找一个字符串在另一个字符串中一共出现了多少次
解题思路:完全的kmp算法加点料 最一开始我是在模式串里面找到匹配串的开头的那个字母然后kmp的,后来果断TLE了,然后改成在找到第一个相匹配的位置后j = next[j - 1],因为要直接用之前在模式串后面部分匹配好的串片段和匹配串前面的相同的串片段。所以省去不少时间。这题挺有助于理解kmp的 之前因为index越过了我设的数组大小导致RE了好几次
代码块
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
using namespace std;
int next[10009];
int index = 0;
string strA, strB;
// 获取一个字串的部分匹配值
void setNext() {
next[0] = 0;
int i = 1, j = 0;
// i指向后缀待比较的最后位置
// j指向前缀待比较的最后位置
while(i < strB.length()) {
// 如果前缀的最后位置和后缀的最后位置相同 则肯定是从0-j位置的串都相同
if(strB[i] == strB[j]) {
// 所以当前位置所生成的后缀与前缀的最长公共长度就是++j
next[i] = ++j;
//这个位置判断完 向下移动一位
++i;
} else if(j!=0) {
// 如果这个i j位置不相同 那么就判断i是否和next[j - 1]的位置的元素是否相同 因为0-next[j - 1]这段的字符串一定和后面 j前面的的后缀字符串相同
j = next[j - 1];
} else {
// 如果j等于零 i和j处的字符串还不对应 说明当前处的字符串后缀不可能与前缀相同 所以当前位置为0 向后移动一位
next[i] = 0;
++i;
}
}
}
int kmp() {
setNext();
int lenA = strA.length();
int lenB = strB.length();
// 从头进行比较
int i = 0, j = 0, sum = 0;// i为模式串当前判断的字符位置 j为匹配串当前判断的字符位置
// 当i或j超出范围即可出现答案
while(i < lenA && j < lenB) {
// 如果匹配串已经退回最后一个字符 不管是否与模式串匹配 都要i++ 如果 两个位置的字符相同的话 那么j++
if(j == 0 || strA[i] == strB[j]) {
if(strA[i] == strB[j]) {
j++;
}
i++;
} else {
j = next[j - 1];
}
if(j == lenB) {
sum++;
j = next[j - 1];
}
}
return sum;
}
int main() {
int n;
cin>>n;
getchar();
while(n--) {
getline(cin, strB);
getline(cin, strA);
int sum = 0;
sum = kmp();
cout<<sum<<endl;
memset(next,0,sizeof(next));
index = 0;
}
return 0;
}