大白书型模板,并没有写成函数形式:
关于p数组(失配指针数组)的含义: - 数组下标从0开始
p[i]表示当发现待匹配串(s)已匹配位置与模板串(t)的t[i]不匹配时,t中最近一个可以尝试继续匹配的位置。即当已经完成了s串与t[0]~t[i-1]的匹配时,发现s串后一个字符与t[i]不匹配,此时s串到当前不构成匹配的位置前可以视为与 t[0] ~ t[ p[i]-1 ] 完成了匹配,因此可以继续匹配s串当前字符与 t[ p[i] ] 。此时如果仍然不匹配可以继续对 t 当前不匹配的位置做失配转跳,直至转跳到t中的当前匹配位置为0位置。
因此实际上p[i]也具有这样的性质:t[0]~t[ p[i]-1 ] 是 t[0]~t[i-1] 的所有前缀中的最长可匹配后缀。
此外,对于一个长度为 m 的串自匹配,在此模板中 m-p[m] 就是这个串的最小循环节长度(最后一个循环可以不完整) - 数组下标从0开始
另一个关于自匹配后的失配指针的性质:对于该串的所有可以作为循环节的长度是:m-p[m] , m-p[p[m]] , m-p[p[p[m]]]……直到 p[x] = 0。 - 数组下标从0开始
这个是下标从0开始的:
#include <stdio.h>
#include <string.h>
typedef long long ll;
#define MP make_pair
#define PB push_back
const int mod = 1e9 + ;
const double eps = 1e-;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6 + ;
const int maxm = 1e5 + ; char s[maxn],t[maxm]; //s为待匹配串,t为模板串
int p[maxn]; //自匹配数组 int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%s%s", t, s); //这个是字符串从下标0开始的
int i,j = ,ans=; // j = 0 注意, ans记录字符串出现次数
int n = strlen(s), m = strlen(t); //在题目中遇到过,其实strlen很慢,所以如果不先存起来可能有TLE的风险
p[] = p[] = ; //初始化自匹配数组
for(i = ; i < m ; ++ i){ //自匹配
while(j && t[i] != t[j])j = p[j];
if(t[i] == t[j])++ j;
p[i+] = j;
}
j = ; //注意 j=0
for(i = ; i < n ; ++ i){ //串匹配
while(j && s[i] != t[j])j = p[j];
if(s[i] == t[j])++ j;
if(j == m){
ans++; //此处记录出现次数(模板串在待匹配串中可重叠),或改为直接break表示是否出现过
}
}
printf("%d\n",ans);
}
return ;
}
数组下标从1开始:
#include<stdio.h>
#include<string.h> const int maxn=1e6+;
const int maxm=1e4+; char s[maxn],t[maxm];
int p[maxm]; int main(){
while(scanf("%s%s",s+,t+)!=EOF){ // 。。这个是从下标1开始的,恩修改的还是比较多的所以不是很建议
int i,j,ans=;
int n=strlen(s+),m=strlen(t+);
p[]=p[]=;
for(i=;i<=m;i++){
j=p[i];
while(j>&&t[i]!=t[j])j=p[j];
p[i+]=t[i]==t[j]?j+:;
}
j=;
for(i=;i<=n;i++){
while(j>&&s[i]!=t[j])j=p[j];
if(s[i]==t[j])j++;
if(j==m+){
ans++;
}
}
printf("%d\n",ans);
}
return ;
}