今天又学了一遍kmp,总算是理解了。虽然这就是一道板子题,但我还想叨叨一会儿。
kmp匹配的大致过程就是对于主串A,我们总想让A[i - j + 1 ~ i]和模式串B的最长前缀B[j]相等。匹配的时候如果A[i]和B[j + 1]不等,就把j = f[j],因为f[j]是保证了A[j - k + 1 ~ j]和B[k]相等的最大的k,那么可以得出对于A[i - 1],B和A[i - 1]的后缀相等的前缀中,最长的是j, 第二长的是f[j],第三长的是f[f[j]]……所以到了A[i],我们就从最长的开始试,如果B[j + 1] != A[i]的话,再试第二长的,看B[f[j] + 1]是否等于A[i],不行的话再看第三长的……所以说,我感觉kmp就是一种贪心……
构造f数组和匹配几乎一样,这里说一点,就是f[1] = 0,然后我们从f[2]开始构造。为啥咧?因为一个串本身也是自己的前缀,如果从1开始,那么f[1] = 1,然后f[2]必定等于f[1] + 1,结果f数组就变成了f[1] = 1,f[2] = 2, f[3] = 3……很明显gg了。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<stack> 9 #include<queue> 10 #include<vector> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 1e4 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), las = ' '; 25 while(!isdigit(ch)) las = ch, ch = getchar(); 26 while(isdigit(ch)) ans = ans * 10 + ch - '0', ch = getchar(); 27 if(las == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) putchar('-'), x = -x; 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 37 char s[maxn], t[maxn * 100]; 38 int f[maxn], cnt = 0; 39 40 void init() 41 { 42 int m = strlen(s + 1); 43 for(int i = 2, j = 0; i <= m; ++i) 44 { 45 while(j && s[j + 1] != s[i]) j = f[j]; 46 if(s[j + 1] == s[i]) j++; 47 f[i] = j; 48 } 49 } 50 void kmp() 51 { 52 cnt = 0; 53 int m = strlen(s + 1), n = strlen(t + 1); 54 for(int i = 1, j = 0; i <= n; ++i) 55 { 56 while(j && s[j + 1] != t[i]) j = f[j]; 57 if(s[j + 1] == t[i]) j++; 58 if(j == m) cnt++, j = f[j]; 59 } 60 } 61 62 int main() 63 { 64 int T = read(); 65 while(T--) 66 { 67 scanf("%s %s", s + 1, t + 1); 68 init(); 69 kmp(); 70 write(cnt); enter; 71 } 72 return 0; 73 }