給定文本串 S 和模式串 P。P 的所有非空真前綴與非空真後綴組成 (n-1)² 個組合串。求 S 中有多少子串等於某個組合串(一個子串可對應多個組合)。
組合串 = 前綴 + 後綴。枚舉相鄰字符對 (ci, ci+1),答案為:
Σ [以 ci 結尾的前綴數] × [以 ci+1 開頭的後綴數]
#include#include #include #include using namespace std; const int MAXN = 50010; int l[MAXN], r[MAXN], f[MAXN]; int dp1[MAXN], dp2[MAXN]; char s1[MAXN], s2[MAXN]; // KMP 失敗函數 + DP 計算前綴數 void getFail(char* p, int* dp) { int m = strlen(p); f[0] = f[1] = 0; dp[0] = 0; dp[1] = 1; for (int i = 1; i < m; ++i) { int j = f[i]; while (j && p[i] != p[j]) j = f[j]; f[i+1] = (p[i] == p[j]) ? j+1 : 0; dp[i+1] = (p[i] == p[j]) ? dp[j+1] + 1 : 1; } } // 匹配過程記錄每個位置的匹配數 void find(char* s1, char* s2, int* dp, int* a) { int n = strlen(s1), m = strlen(s2); int j = 0; for (int i = 0; i < n; ++i) { while (j && s1[i] != s2[j]) j = f[j]; if (s1[i] == s2[j]) ++j; a[i+1] = dp[j]; if (j == m) { a[i+1]--; j = f[j]; } } } int main() { int T; scanf("%d", &T); for (int cas = 1; cas <= T; ++cas) { scanf("%s %s", s1, s2); int n = strlen(s1), m = strlen(s2); getFail(s2, dp1); find(s1, s2, dp1, l); reverse(s1, s1+n); reverse(s2, s2+m); getFail(s2, dp2); find(s1, s2, dp2, r); long long ans = 0; for (int i = 1; i < n; ++i) ans += 1LL * l[i] * r[n-i]; printf("Case %d: %lld\n", cas, ans); } return 0; }
dp[i] 記錄以 i-1 結尾的前綴出現次數a[i+1]-- 避免重複計數本題巧妙結合 KMP 字符串匹配與動態規劃計數,是字符串問題中「正反雙向 + 狀態增強」的經典範例。