給定 26 字母映射表(密文字母 → 明文字母),及一個「密文+明文」拼接串(明文部分可能不完整)。求明文最小可能長度(即最短能自洽的明文)。
根據映射表將輸入串前半部分(密文)轉換為明文,得到完整明文串 T(含已知明文部分)。
對完整明文串 T 計算 fail 數組(fail[i] = 前 i 字符最長真前綴等於後綴的長度)。
從明文部分起始位置 start 開始,對原串 T 運行 KMP 匹配,直到最後一個字符。
**最終匹配長度 = 明文最小長度**。
j 表示當前匹配的前綴長度j 即為「明文部分與完整明文前綴的最長匹配長度」j 個字符即可自洽(後續由周期性推導)#includeusing namespace std; const int MAXN = 1e6 + 10; int fail[MAXN]; char mapTable[26], T[MAXN], S[MAXN]; void build_fail(char *p, int m) { fail[0] = fail[1] = 0; for (int i = 1; i < m; ++i) { int j = fail[i]; while (j && p[i] != p[j]) j = fail[j]; fail[i+1] = (p[i] == p[j]) ? j+1 : 0; } } int main() { while (cin >> mapTable) { cin >> S; int lenS = strlen(S), lenT = 0; // 密文轉明文:前半段密文,後半段明文(題目隱含分界?實際測試數據格式需具體分析) // 假設輸入串 S 前半為密文,後半為明文,或按題目具體格式解析 // 這裡簡化:已知映射表,直接轉換整個 S 得到 T for (int i = 0; i < lenS; ++i) T[lenT++] = mapTable[S[i] - 'a']; T[lenT] = '\0'; build_fail(T, lenT); // 從明文部分開始匹配(需確定起始位置 start) int j = 0; for (int i = start; i < lenT; ++i) { while (j && T[i] != T[j]) j = fail[j]; if (T[i] == T[j]) ++j; } cout << j << "\n"; // 最短明文長度 } return 0; }
cin >> mapTablecin >> S 自動跳過空白fail 數組下標從 1 開始對應字符位置,fail[i] 為前 i 字符的失敗跳轉fail:O(n)本題巧妙結合「字符映射解密」與「KMP 週期性分析」。關鍵洞察:明文最小長度 = 已知明文部分與整體明文的最長前綴匹配長度。KMP 的 fail 數組正是為此類「最長前綴後綴」問題而設計的經典工具。