題意
題解
dp與kmp的巧妙結合。
設文本串s長度爲,模式串t長度爲。題面中赤裸裸地告訴你,這就擺明了複雜度應該在這個級別,這個的複雜度肯定是掃一遍s,至於,可以猜想是對於s的每個位置進行暴力的匹配。
我們可以考慮用dp來解決這個問題。設表示t在s的前個位置最大的出現次數。那麼如果一個位置想從之前的位置轉移過來,就必須滿足t能在這個位置與s匹配,這一部分可以暴力判斷。
具體怎麼轉移呢?首先很明顯可以直接從轉移過來,表示這段放一個完整的t。
但是這還不夠,因爲有可能在這個位置之前連續而重疊地放了好幾個t,也就意味着新放進去地這個t並不是完整地,而是和上一個t的後綴重疊構成的。那麼這就需要滿足t的一段後綴和一段前綴相等。
這就令我們想到了kmp算法中的next數組。我們可以通過從m開始一直跳next,來保證前綴與後綴相等。
但是又有一個問題,假設我們現在長度爲的前後綴相等,我們卻不能直接從轉移,因爲的定義並不能保證這個位置上一定放了t。
所以我們再定義一個,表示s的前個位置,強制最後放一個t的最大出現次數。那麼這樣我們上面的情況就可以通過之間的轉移來實現了。即,一直跳next更新即可。
轉移完之後,我們再令,也就是考慮放和不放t兩種情況。
代碼
#include <bits/stdc++.h>
#define MAX 100005
using namespace std;
char s[MAX], t[MAX];
int n, m;
int Next[MAX], f[MAX], g[MAX];
bool chk(int p){
for(int j = 1; j <= m; j++){
if(s[p-j+1] != t[m-j+1] && s[p-j+1] != '?') return false;
}
return true;
}
int main()
{
scanf("%s%s", s+1, t+1);
n = strlen(s+1), m = strlen(t+1);
for(int i = 2, j = 0; i <= m; i++){
while(j && t[j+1] != t[i]) j = Next[j];
if(t[j+1] == t[i]) j++;
Next[i] = j;
}
for(int i = 1; i <= n; i++){
f[i] = f[i-1];
if(chk(i)){
g[i] = f[i-m]+1;
for(int j = Next[m]; j; j = Next[j]){
g[i] = max(g[i], g[i-(m-j)]+1);
}
f[i] = max(f[i], g[i]);
}
}
cout << f[n] << endl;
return 0;
}